一、相关概念
1.1程序.
- 程序的一次执行过程,或是正在内存中运行的应用程序。如:运行的Eclipse、QQ音乐等。
- 每个进程都有一个独立的内存空间,系统运行一个程序即是一个进程从创建、运行到消亡的过程(生命周期)。
- 进程到运行不仅仅需要CPU,还需要很多其它资源,比如:内存、显卡、磁盘等。
- 程序是静态的,进程是动态的。
- 进程是操作系统调度和分配资源的最小单位(亦是系统运行程序的基本单位。一定要和线程区分开),系统在运行时会为每个进程分配不同的内存区域和时间片。操作系统调度器:拆分CPU为一段段时间片的运行片,轮流分配给不同的程序。
- 现代的操作系统,大都是支持多进程的,支持同时运行多个程序。比如:一边打游戏,一边使用QQ聊天,一边使用录屏软件,同时还开着画图板,dos窗口等软件。
- 不同进程之间是不共享运行时内存的,但是进程和进程之间是可以进行数据交互和通信,成本很高。如:在京东里面通过微信付款、在淘宝里面通过支付宝付款。
- 1.3线程.
- 线程(thread):
- 进程可进一步细化为线程,是程序内部的一条执行路径。一个进程中至少有一个线程。
- 一个进程同一时间若并行执行多个线程,就是支持多线程的。
- 线程作为CPU调度和分配的最小单位(一定要和进程区分开)。有句话说CPU只能看到线程,可以这么理解,假设我是CPU,我闭着眼,操作系统调度器将一个进程分配给我之后,我拿到进程睁开眼,我看到的是什么?我看到的是进程中的很多线程,那么我现在能调度和分配的是什么?进程?不行,因为我看不到其他进程,何来调度分配,只能调度我看到的那些线程,如果我是4核的话,把线程ABCD分配到核心1234,其他的线程依然要等待分配,至于等待多久,如何分配,暂不在本文讨论范围。于是线程是CPU调度和分配的基本单位。
- 一个进程中的多个线程共享相同的内存单元,它们从同一个堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。
- 下图中,红框的蓝色区域为线程独享,黄色区域为线程共享。
1.4查看进程和线程.- 电脑底部任务栏,右键----->打开任务管理器,可以查看当前任务的进程。
- 1. 每个运行的应用程序都是一个进程:
2. 一个应用程序的多次运行,就是多个进程:
3. 一个进程中包含多个线程:
- 1.5线程调度.
- 分时调度:所有线程轮流使用 CPU 的使用权,并且平均分配每个线程占用 CPU 的时间。
- 抢占式调度:让优先级高的线程以较大的概率优先使用 CPU。如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
- 1.7单核CPU和多核CPU.
- 单核CPU,在一个时间单元内,只能执行一个线程的任务。例如,可以把CPU看成是医院的医生诊室,在一定时间内只能给一个病人诊断治疗。所以单核CPU就是,代码经过前面一系列的前导操作(类似于医院挂号,比如有10个窗口挂号),然后到cpu处执行时发现,就只有一个CPU(对应一个医生),大家排队执行。这时候想要提升系统性能,只有两个办法,要么提升CPU性能(让医生看病快点),要么多加几个CPU(多整几个医生),即为多核的CPU。
- 问题:多核的效率是单核的倍数吗?譬如4核A53的cpu,性能是单核A53的4倍吗?理论上是,但是实际不可能,至少有两方面的损耗。
- 一个是多个核心的其他共用资源限制。譬如,4核CPU对应的内存、cache、寄存器并没有同步扩充4倍。这就好像医院一样,1个医生换4个医生,但是做B超检查的还是一台机器,性能瓶颈就从医生转到B超检查了。
- 另一个是多核CPU之间的协调管理损耗。譬如多个核心同时运行两个相关的任务,需要考虑任务同步,这也需要消耗额外性能。好比公司工作,一个人的时候至少不用开会浪费时间,自己跟自己商量就行了。两个人就要开会同步工作,协调分配,所以工作效率绝对不可能达到2倍。
- 参考资料:多核CPU和多个CPU的区别:https://www.zhihu.com/question/20998226。
- 1.8并行与并发.
1.8.1并行. - 并行(parallel):指两个或多个事件在同一时刻发生(同时发生)。指在同一时刻,有多条指令在多个CPU上同时执行。比如:多个人同时做不同的事。
- 比如,A、B、C三个指令同时执行:
- 1.8.2并发.
- 并发(concurrency):只是一种现象,指两个或多个事件在同一个时间段内发生。即在一段时间内,有多条指令在单个CPU上快速轮换、交替执行,使得在宏观上具有多个进程同时执行的效果。
- 比如,A、B、C三个指令在同一时间段内快速切换执行:
解释一:
多线程并发只是表面和感觉上的并发,并不是实质上的并发。一个线程要运行,它必须占有CPU,而我们目前用的计算机大多都是单CPU的,所以一次最多只能有一个线程获取CPU并运行。
多线程的实质是“最大限度地利用CPU资源”,当某一个线程的处理不需要占用CPU而只需要和I/O等资源打交道时,让其他线程有机会获得CPU资源。这有点类似于“统筹方法”,例如让你打扫房子和烧水,要在最短的时间内做好这两件事,你一定会想到先把水烧上,然后在等水烧开的空闲时间中去打扫房子,而不是先打扫好了再去烧水,也不是先烧好了再去打扫,这个例子里面,你是那个唯一的CPU,而烧水和打扫就是两个线程。
虽然CPU只有一个,但是它在多个线程之间频繁切换,当切换的频率高到一定程度时,我们就感觉所有的线程在同时运行,于是感觉这多个线程是并发的。因此,并发并不是真的指多个线程同时运行,它仅仅只是形容一种现象。
解释二:
在操作系统中,启动了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单核 CPU 系统中,每一时刻只能有一个程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那是因为分时交替运行的时间是非常短的。宏观并行,微观其实是串行(排队)。 而在多核 CPU 系统中,则这些可以并发执行的程序便可以分配到多个CPU上,实现多任务并行执行,即利用每个处理器来处理一个可以并发执行的程序,这样多个程序才可以真正的同时执行。目前电脑市场上说的多核 CPU,便是多核处理器,核越多,并行处理的程序越多,能大大的提高电脑运行的效率。
- 1.8.3总结.
- 并发只是一种程序在运行时的现象效果(宏观并行,微观串行),和串行、并行没有可比性。串行即排队,并行才是真正的多个程序同时执行。
- 单CPU中进程只能是并发,多CPU计算机中进程可以并行也可以并发。
- 单CPU单核中线程只能并发,单CPU多核中线程可以并行也可以并发。
- 无论是并发还是并行,使用者来看,看到的是多进程,多线程。
- 用户线程:AVA语言中无论是线程还是线程池,默认都是用户线程,用户线程也被称为普通线程。
- main线程也是用户线程,又叫主线程,是由JVM创建的。
- 用户线程创建的子线程默认是用户线程,可以设置为守护线程:setDaemon(true)。比如在main()方法(主线程)里面创建的线程就是子线程。
- 守护线程:也被称之为后台线程、服务线程或精灵线程。
- 守护线程是为用户线程服务的,当线程中的用户线程都执行结束后,守护线程也会跟随结束。
- 守护线程具有自动结束生命周期的特性,而非守护线程则不具备该特性。
- 守护线程的优先级和用户线程优先级一致;
- 守护线程的子线程也是守护线程。
- 垃圾回收线程就是典型的守护线程,随主线程结束而结束。
- 设置用户线程为守护线程方法:setDaemon(true) ,该方法必须在 start()方法之前设置, 如果设置在 start() 之后,程序执行会报错,守护线程也不会生效。
- 判断线程是否是守护线程方法:isDaemon();
- JVM必须确保所有的用户线程执行结束之后才能退出;如果只还有守护线程在运行,JVM不用等待守护线程执行结束,会直接退出。验证代码如下:
二、创建线程的方式.
2.1概述.
- Java语言的JVM允许程序运行多个线程,使用 java.lang.Thread 类代表线程,所有的线程对象都必须是Thread类或其子类的实例。
- Thread类的特性:
- 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,因此把run()方法体称为线程执行体。
- 通过该Thread对象的start()方法来启动这个线程,而非直接调用run(),要想实现多线程,必须在主线程中创建新的线程对象。
2.2创建线程的几种方式.
- 继承Thread类;
- 实现Runnable接口;
- 实现Callable接口(JDK1.5出现的);
- 使用线程池(JDK1.5出现的)。
3.继承Thread类方式.
3.1使用步骤.
- 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务;
- 创建Thread子类的实例,即创建了线程对象;
- 调用线程对象的start()方法来启动该线程。
3.2代码演示.
- 创建 Thread类的子类:
- 测试类:
- 线程运行效果图: