1.概念
程序
存储在磁盘上的一段指令的集合,是静态的。
进程
运行中的程序,指计算机在某个数据集合上的一次运行活动,每个进程都有独立的资源空间,是操作系统进行资源分配与调度的基本单位。
(数据集合包含两方便,一是数据,二是硬件资源,如CPU,IO等)
线程
又称轻量级进程,是为了提高资源利用率以及CPU的并发度,而对进程进行的一种优化。线程是进程中的一个个的实体,是进程的一个个的执行单元。同一进程的线程共享该进程的资源,即电脑所分配的资源空间。
主线程、父线程、子线程
JAVA程序的入口是main方法,main方法运行时对应产生一个主线程。A线程中创建B线程,A线程称为B线程的父线程,B线程称为A线程的子线程。
多线程的最终目的
即充分利用CPU,当某一线程不需要CPU而只和IO等资源打交道时,让需要占有CPU资源的其他线程有机会获得CPU,产生一个复用CPU的效果。比如一边听歌,一边编辑文档。
多线程中的概念其实都是操作系统中进程管理的相关内容,需要一定的操作系统的理论知识才能更好的理解。如果在学习JAVA多线程的相关内容时觉得晦涩难懂,建议可以先去成体系的学习一下操作系统中进程管理和内存管理两块内容。手撕JAVA系列后续会更新操作系统专题的系列文章,深入浅出、成体系的介绍进程、线程的相关内容,届时会详细论述一些问题,如进程和线程的具体关系,为什么会出现线程等大家经常疑惑的点。
2.状态
在操作系统理论中规定了进程的三种标准状态:就绪态、运行态、阻塞态。
由于进程是线程的载体,所以对应着线程也有这些状态。
在JAVA的落地实现中,基于这三种状态进行了扩写,总共实现了6种状态:
新创建:创建了线程对象,未调用start()之前。
可运行:包含ready、running两个状态,调用start()后,分到时间片running,未分到则ready。
被阻塞:申请的资源正在被其他线程独占(争抢锁失败),进入阻塞队列,资源空闲后唤醒。
等待:线程执行了wait(),join(),线程会进入等待状态,直到发来唤醒信号(notify())为止。
计时等待:指定等待时间,在该时间内线程处于等待。
3.基本操作
新建线程
1.继承Thread类
继承Thread类,重写run(),通过调用start()来启动。
2.实现Runable接口
实例化实现接口的类,创建一个Thread类,将实例化的实现Runable的类,传给Thread的构造方法。本质上也是通过thread的类来实现。通过调用start方法来启动。
获取状态
getState()
获取当前线程
currentThread()
设置/获取线程名称
setName()/getName()
isAlive
isAlive(),判断当前线程是否处于活动状态。
中断
线程在JAVA层面是没有办法直接中断的,JAVA层面的线程中断都只是为线程设置一个状态,方便代码在逻辑中进行操作。
Thread类中有两个方法与线程中断有关:
1.interrupt()
将中断标志位设置为true,前提条件是该线程必须是处于运行状态(非等待、睡眠)
2.interrupted()
判断线程是否被中断并且清除中断标志位(true—>false)
join
join(),加入,Thread类自带方法。
调用join(),该线程进入waiting状态,等待notify()或者加入的线程赶上,再转为runnable。
join()可以看做是线程间协作的一种方式,很多时候,一个线程的输入可能非常依赖于另一个线程的输出,如果一个线程实例A执行了threadB.join(),其含义 是:当前线程A会等待threadB线程终止后threadA才会继续执行。
sleep
sleep(),休眠,Thread类自带方法。
让线程按照指定的时间休眠,其休眠时间的精度取决于处理器的计时器和调度器。休眠的线程如果获得了锁,sleep()并不会失去锁,只会让出CPU。
wait
wait(),等待,Object类自带方法。
调用wait()的前提是线程已经获得锁,因此规定只能在同步方法和同步块中调用。
调用wait(),释放线程所占有的锁,该线程变为waiting状态,进入等待池中,等待Object.notify()/Object.notifyAll()通知后,才会离开等待池去争抢CPU,并且 再次获得CPU时间片才会继续执行。
yield
yield(),让步,Thread类自带方法。
让线程让出本次CPU时间片,重新去等待下一次CPU时间片,让出的时间片分配给当前线程相同优先级的线程
setPriority
Thread类的int型成员变量Priority表示线程的优先级,优先级的范围从1~10.在构建线程的时候可以通过setPriority(int)方法进行设置,默认优先级为5,值越 大,线程的优先级越高。优先级高的线程相较于优先级低的线程更容易优先获得处理器时间片,但并不能保证优先级高的一定先获得处理器时间片。
线程的优先级设置不当或者滥用可能会导致某些线程永远无法得到时间片资源运行,从而产生线程饥饿。
Daemon
Daemom,守护线程,可以理解为与应用同生共死的线程,应用只剩守护线程在运行时,JVM会结束该应用。
守护线程主要用来守护一些系统级别的资源,作为应用的兜底,应用中只要业务线程结束,那么整个应用就结束,不必等待设置为守护线程的线程结束。
并不会进入死循环