-
操作系统线程状态
- 初始状态:线程已经被创建但是不允许分配cpu执行,在编程层面线程已经创建,但在操作系统层面真正的线程还没有创建
- 可运行状态:可以分配cpu执行此线程,这种状态下操作系统层面线程已经创建,所以可以分配cpu执行
- 运行状态:当有空闲线程执行时,线程状态变成运行状态,没有空闲cpu执行时为可运行状态
- 休眠状态:当调了阻塞或等待的api时,会释放cpu使用权,等待被唤醒,线程状态变成休眠状态,当等待的事件出现了,线程就会从休眠状态转换到可运行状态。
- 终止状态:线程执行完或者出现异常就会进入终止状态,终止状态的线程不会切换到其他任何状态,进入终止状态也就意味着线程的生命周期结束了。
-
java层面的线程状态
- NEW:初始化状态
- RUNNABLE:运行状态包括可运行状态与运行状态
- BLOCKED:阻塞状态【只针对于syncronized竞争没拿到锁】
- WAITING:无时限等待
- TIMED_WAITING:有时限等待
- TERMINATED:终止状态
-
java线程的创建方式【本质只有一种,都是通过new Thread()创建线程,调用Thread#start启动线程,最终都会调用Thread#run方法】
-
继承thread类,底层使用的runable接口
// 创建线程对象 Thread t = new Thread() { public void run() { // 要执行的任务 } }; // 启动线程
-
实现runnable接口配合thread类,run()方法没有返回值,不抛出异常
Runnable runnable = new Runnable() { public void run(){ // 要执行的任务 } }; // 创建线程对象 Thread t = new Thread( runnable ); // 启动线程
-
实现callable接口结合线程池使用,有返回值,抛异常,线程池里面的线程也是new thread实现的
class CallableTask implements Callable<Integer> { @Override public Integer call() throws Exception { return new Random().nextInt(); } } //创建线程池 ExecutorService service = Executors.newFixedThreadPool(10); //提交任务,并用 Future提交返回结果
-
使用lamda表达式
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
-
-
内核级线程与用户级线程
- 内核级线程【java属于内核线程】:它们是依赖于内核的,即无论是用户进程中的线程,还是系统进程中的线程,它们的创建、撤消、切换都由内核实现。
- 用户级线程:操作系统内核不知道应用线程的存在。
-
java线程调度机制
- 协同式线程调度:线程执行时间由线程本身来控制
- 线程把自己的工作执行完之后,要主动通知系统切换到另外一个线程上。
- 好处:实现简单,且切换操作对线程自己是可知的,没啥线程同步问题
- 坏处:线程执行时间不可控制,如果一个线程有问题,可能一直阻塞在那里。
- 抢占式线程调度【java调度就是抢占式线程调度】
- 每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定【(Java中,Thread.yield()可以让出执行时间,但无法获取执行时间)】
- 好处:线程执行时间系统可控,也不会有一个线程导致整个进程阻塞。
- 协同式线程调度:线程执行时间由线程本身来控制
-
thread的常见方法
-
sleep方法
- 线程状态从Running进入TIMED_WAITING状态
- 基于线程,不释放对象锁
- 清除中断标识,需捕获中断异常
- 睡眠结束的线程必定会得到cpu执行权
- 当参数传0时,效果与yield相同
-
yield方法
- 释放cpu资源,线程状态从running进入runnable状态
- 基于线程,不释放对象锁
- 会清除中断标志,需捕获中断异常
- 当前只有一个main方法时,调用yield方法后main方法会立即执行,因为没有比他优先级更高的线程
- 具体执行完全依赖操作系统调度
-
join方法
-
主线程调子线程的join方法,主线程会等到子线程执行完成之后才会执行【基于等待通知】
-
代码实现
public class ThreadJoinDemo { public static void main(String[] sure) throws InterruptedException { Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("t begin"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t finished"); } }); long start = System.currentTimeMillis(); t.start(); //主线程等待线程t执行完成 t.join(); System.out.println("执行时间:" + (System.currentTimeMillis() - start)); System.out.println("Main finished"); }
-
-
await方法
- 会释放锁,清除标志位【基于对象,对象都实现了monter机制,会释放锁】
-
stop方法
- 已经被jdk废弃,原因就是stop()方法太过于暴力,强行把执行到一半的线程终止。
- stop会释放对象锁,可能会造成数据不一致。
-
-
Java线程的中断机制
-
中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理
-
API的使用
- interrupt():将线程中断标志位设置成true,不会停止线程
- isInterrupted(): 判断当前线程的中断标志位是否为true,不会清除中断标志位
- Thread.interrupted():判断当前线程的中断标志位是否为true,并清除中断标志位,重置为fasle,只会生效一次
-
一些坑
-
sleep可以被中断 抛出中断异常:sleep interrupted, 清除中断标志位
-
wait可以被中断 抛出中断异常:InterruptedException, 清除中断标志位
-
处于休眠中的线程被中断,线程是可以感受到中断信号的,并且会抛出一个 InterruptedException 异常,同时清除中断信号,将中断标记位设置成 false。需要在catch中重新手动添加中断信号,不做任何处理,就会屏蔽中断请求,有可能导致线程无法正确停止。
-
-
优雅停止线程
-
利用中断机
while (!Thread.currentThread().isInterrupted() && more work to do) { do more work }
-
使用中断机制时一定要注意是否存在中断标志位被清除的情况
-
-
-
等待唤醒机制
- wait方法
- monter机制实现的,需要再同步代码块中实现,没有绑定线程
- notify方法
- monter机制实现的,需要再同步代码块中实现,没有绑定线程
- wait方法
-
LockSupport等待唤醒
-
概念:LockSupport是JDK中用来实现线程阻塞和唤醒的工具,线程调用park则等待许可,调用unpark则为指定线程提供许可
-
park:等待一个许可,有许可后可以一直执行下去
-
unPark:入参是一个线程,为指定线程parkTread提供许可,如果在park之前执行,线程在park时可以拿到许可继续执行
-
线程生命周期&线程交互方式
最新推荐文章于 2024-11-03 11:01:20 发布