并发编程(一):并发编程概念

2021-05-31  本文已影响0人  圆企鹅i

1.进程和线程

进程:由指令和数据组成,指令加载到CPU,数据加载到内存,那么进程启动。
进程栗子:浏览器,记事本...可以同时开多个 那么就是多进程

线程:一个线程就是一个指令流,线程将指令按顺序交给CPU
线程栗子:在记事本(进程)中保存,线程将保存的代码交给CPU,CPU执行IO操作。
注:java中线程作为最小调度单位进程作为资源分配的最小单位
注2:windows中进程是不活动的,只是线程的容器


单核心CPU:

image.png
多核心CPU:
image.png

并发
Rob Pike(golang语言创始者):
并发(concurrent)是同一时间应对(dealing with)多件事情的能力。
并行(parallel)是同一时间动手做(doing)多件事情的能力。


并发:多线程问题,微观上单核心还是逐个执行每一个小指令
并行:栗子--java stream并行流 同时调度CPU所有核心来处理数据


2.为什么多线程可以提高效率?

2.1 同步和异步

1.同步
一般我们方法执行都是主线程在执行,方法会从上到下依次运行,上面的代码没有运行完后面的代码是不能运行的(同步的概念

2.异步
多线程情况下,那么可以让新的线程去处理较为耗时的任务,主线程继续运行(异步的概念)。

栗子:用户点击执行某个复杂任务,如果不是多线程,那么必须等待执行任务的代码结束 才能告知用户这个指令执行完成。多线程可以让新线程去处理复杂的任务,主线程继续跑,跑完主线程告诉返回给用户,刚刚那个指令已经开启了。(异步

3.线程创建方法

3.1 new Thread
       //new thread
        Thread newThread = new Thread("new_thread"){
            @Override
            public void run() {
                log.info("print new thread and override run");
            }
        };
        newThread.start();
        //new thread by lambda
        Thread newThreadLambda = new Thread(() -> log.info("print new thread override run by lambda"));
        newThreadLambda.setName("new_thread_lambda");
        newThreadLambda.start();
3.2 Runnable
        //特点:任务和线程分开 写起来方便一些 毕竟实际中执行都是使用线程池
        //new runnable
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                log.info("print new runnable thread and override run");
            }
        };
        Thread runnableThread = new Thread(runnable);
        runnableThread.start();
        //new runnable by lambda
        Runnable runnableLambda = () -> log.info("print new runnable thread and override run by lambda");
        Thread runnableLambdaThread = new Thread(runnableLambda);
        runnableLambdaThread.start();
3.3 Callable
        //特点:继承了runnable(配合线程) 和 future(能够返回值,抛出异常)
        //futureTask
        FutureTask<Object> futureTask = new FutureTask<>(new Callable<Object>() {
            @Override
            public String call() throws Exception {
                log.info("print new future task thread and override call");
                Thread.sleep(3000);
                return "FutureTask return";
            }
        });
        new Thread(futureTask, "futureTask_thread").start();
        //注意:main线程中 get方法要等call方法执行完 才能拿到返回值 才会执行
        Object result = futureTask.get();
        log.warn("result = {}", result.toString());
        // futureTask by lambda
        FutureTask<Object> futureTaskLambda = new FutureTask<>(() -> {
            log.info("print new future task thread and override call by lambda");
            Thread.sleep(5000);
            return "FutureTask lambda return";
        });
        new Thread(futureTaskLambda, "futureTask_threadLambda").start();
        Object resultLambda = futureTaskLambda.get();
        log.warn("resultLambda = {}", resultLambda.toString());

4. 线程运行原理

4.1 栈

栈:包含:局部变量表,操作数栈,动态链接,方法返回值和一些附加信息
ps:在debug的时候我们看到的哪些属性 都是idea从栈帧里面获取的

image.png

线程启动--虚拟机为线程开辟一块栈内存
线程的每个方法运行在一个栈帧之上

image.png
4.2 线程的上下文切换 Context Switch

概念:因为一些原因导致cpu不在执行当前线程了,转而执行另一个线程代码

4.2.1 线程的cpu时间片用完
4.2.2 垃圾回收

因为垃圾回收会让所有线程停下来,
所有我们需要对jvm调优,来减少垃圾回收的次数

4.2.3 有更高优先级的线程需要运行
4.2.4 线程自己调用了sleep,yield,wait等方法

注意:上下文切换会操作系统帮你保存线程状态,包括:让程序计数器将指令地址记住(运行到哪一行了),局部变量,返回地址等 频繁的上下文切换 会

5. 线程安全问题

关键点:多个线程处理共享资源
关键点2:没有开多线程,每个方法有自己的线程栈,也就没线程安全问题(常规情况下)

image.png
image.png

感谢:
《Java并发编程的艺术》方腾飞,魏鹏,程晓明
《Java并发实现原理:JDK源码剖析》余春龙
全面深入学习java并发编程,java基础进阶中级必会教程_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili

上一篇下一篇

猜你喜欢

热点阅读