文章目录
- 前言
- 一、 线程与进程
- 1.1 什么是线程与进程?
- 1.2 并发与并行
- 1.3 同步调用与异步调用
- 1.4 为什么要使用多线程?
前言
在学习juc前,需要先对进程和线程之间整体有一个认知。我们之前或多或少接触过,一些特别高大上的概念,比如多线程,高并发 之类的。该文就是先对这些概念整体理清,为后续学习扫清障碍。
一、 线程与进程
1.1 什么是线程与进程?
进程:
-
进程是用来存放数据与指令等运行程序的组成部分,然后指令运行的时候还需用到磁盘、IO设备,网络等,其中进程就是用来加载指令,管理内存·,管理IO的。
-
程序运行的时候,从磁盘 -> 内存就代表一个进程开启了
-
进程可以看作一个程序的示例,比如我们打开任务管理器:
这表明,对于应用来说,可以运行一个或者多个进程。
线程:
- 一个进程包括多个线程
- 一个线程就是一个指令流,将指令流的一条条指令以一定顺序交给CPU执行
- Java中,线程是最小的调度单位,进程作为资源分配的最小单位。windows中,进程不活动,只是作为线程的容器。
二者对比 :
- 进程拥有共享的资源,比如内存空间的,供子集线程共享
- 进程间的通信较为复杂
- 同一台计算机的进程通信称为IPC,比如套接字、消息队列等。
- 不同计算机的通信要通过网路,并遵守共同的协议,比如http等。
- 线程通信比较简单,因为它们可以共享进程内的内存,一个例子是多个线程访问一个共享变量。
- 线程更轻量级,上下文切换成本较低。
1.2 并发与并行
- 并发:两个及两个以上的任务在同一时间段执行
- 并行:两个及两个以上的任务在同一时刻执行
并发 :
在单核cpu中,线程实际是串行执行的。操作系统中的组件,叫做任务调度器,将cpu的时间片(windows下接近15ms)分给不同的线程使用,只是由于cpu在线程间的切换非常快,导致体感上是同时运行的。
总结一句话:微观串行,宏观并行,但是,实际上还是串行。
并行:
在多核cpu中,每个核心可以处理一个线程,这样就可以真正意义上实现并行,即同一时刻,同时运行多个线程。
如图:
1.3 同步调用与异步调用
注意 :
代码中的Constants.MP4_FULL_PATH就理解为一个文件即可。
该代码作用就是花时间读取一个文件。
- 同步调用:
- 异步调用
从运行结果,可以看出来,异步调用是通过一个Thread-0的线程执行的。
从上面的运行结果可以得到两者的含义。
同步调用:发出一个调用之后,结果没有返回的话,该调用就不可以返回,一直等待。
异步调用:调用在发出后,不用等待返回结果,调用直接返回。
其实简单来说:
异步调用就是在main线程里面新开一个线程,并且新开的线程执行与否对于主线程接下来往下执行没有影响。
同步调用就是一直只执行当前线程呗,不开其它的线程。
根据这个,我们就可以引出下面的多线程:
- 多线程的作用:
多线程可以让方法执行变成异步的(即不要干等着)。比如读取磁盘文件时,需要5秒,那如果我们不使用多线程,这5秒什么都做不了只能干等着。
1.4 为什么要使用多线程?
-
在单核时代,使用多线程可以提高cpu和io系统的运行效率,如果只运行了一个java进程的情况下,当线程进行读取io的操作,此时线程被阻塞。如果没有多线程,那么此时整个进程都被阻塞, 无法使用cpu,系统整体效率只有50%。但是如果使用多线程的话,一个线程阻塞,其它线程还可以继续访问,大大提高系统运行效率。但是需要理解的是,单核中,多线程并不起到减少时间的作用。(单核只作了解)
-
在多核时代。多线程主要是为了提高进程利用多核cpu的能力。
eg : 处理一个复杂的任务,如果只有一个线程,那么只有一个核心被利用到。失去了多核的作用,但是多个线程就可以同时使用多个核心同时处理任务,提高了解决任务的效率,时间约等于(单核时执行的时间 / cpu核心数)。
实例:
java">@Benchmark
public int c() throws Exception {int[] array = ARRAY;FutureTask<Integer> t1 = new FutureTask<>(()->{int sum = 0;for(int i = 0; i < 250_000_00;i++) {sum += array[0+i];}return sum;});FutureTask<Integer> t2 = new FutureTask<>(()->{int sum = 0;for(int i = 0; i < 250_000_00;i++) {sum += array[250_000_00+i];}return sum;});FutureTask<Integer> t3 = new FutureTask<>(()->{int sum = 0;for(int i = 0; i < 250_000_00;i++) {sum += array[500_000_00+i];}return sum;});FutureTask<Integer> t4 = new FutureTask<>(()->{int sum = 0;for(int i = 0; i < 250_000_00;i++) {sum += array[750_000_00+i];}return sum;});new Thread(t1).start();new Thread(t2).start();new Thread(t3).start();new Thread(t4).start();return t1.get() + t2.get() + t3.get()+ t4.get();
}
@Benchmark
public int d() throws Exception {int[] array = ARRAY;FutureTask<Integer> t1 = new FutureTask<>(()->{int sum = 0;for(int i = 0; i < 1000_000_00;i++) {sum += array[0+i];}return sum;});new Thread(t1).start();return t1.get();
}
运行结果:
上述代码含义就是 :
4个线程(t1、t2、t3、t4),每个线程调用方法执行2500万次,打印信息中是c开头的。
d开头方法是一个方法调用1亿次。
c为异步调用,d为同步调用,从结果来看,c方法的效率显著高于d方法(Score越小,效率越高)。
但是如果是在单核cpu中的话,不会出现性能的提升,因为单核的本质上还是串行,并不会产生并行的效果。