1.线程状态
java中定义的线程状态有6种,从jdk的源码类java.lang.Thread.State中可以看到,一张图搞定java中线程的状态。
线程在其执行生命周期中,状态是在不停变得,符合大学中操作系统的线程运行状态切换,不详述。
2.线程中止
stop : 直接终止线程,并且清除监控锁的信息,但是可能导致线程安全问题,jdk不建议使用。
interrupt:目标线程在调用类的wait,join、sleep方法时被阻塞,那么interrupt会生效,该线程的中断状态将被清除,抛出InterruptedException异常。
如果目标线程被I/O或者NIO中的Channel所阻塞,同样,I/O操作会被中断或者返回特殊异常值。达到终止线程的目的。
如果以上条件不满足,则会设置此线程为中断状态。
推荐使用标志位flag控制线程的终止。
3.线程通信
jdk提供的线程协调API的三种方式: suspend/resume、wait/notify、park/unpark。
使用场景,
经典场景:生产者-消费者模型。(线程阻塞、线程唤醒)
去海某捞吃火锅,很火爆,需要排队等位。
我们去吃火锅 ——》人太多,没位子,进入等待----》叫号通知我们有位置吃火锅。
suspend/resume:suspend挂起线程,resume唤醒线程。
jdk不推荐使用,容易造成死锁。
场景1: resume启动比suspend快,造成死锁。
场景2:suspend 使用synchronized锁一个类对象,resume拿不到这个类锁造成死锁。
示例
public void suspendResumeDeadLock() throws InterruptedException {
Position position = null;
Thread consumer = new Thread(() -> {
//没有位子进入等待
while (position == null) {
synchronized (this) {
System.out.println("1 进入等待");
Thread.currentThread().suspend();
}
System.out.println("3 有位子,吃火锅");
}
});
consumer.start();
Thread.sleep(3000L);
System.out.println("2 叫号,有位子了,请某某准备就餐");
consumer.resume();
//等位3秒后有位子,这种方式拿不到锁,造成死锁
// synchronized (this){
// consumer.resume();
// //通知顾客有位置了
// System.out.println("2 叫号,有位子了,请某某准备就餐");
// }
wait/notify/notifyAll:
这些方法都只能由同一个对象锁的持有者线程调用,也就是必须写在synchronize同步代码块里,否则会抛出IllegalMonitorStatException异常。
wait方法: 导致当前线程进入等待,加入该对象的等待集合中,并且放弃当前持有的对象锁。
notify/notifyAl方法:唤醒一个或正在等待这个对象锁的线程。
注意:wait方法会自动解锁,但是对顺序有要求,如果在notify被调用之后,才开始wait方法的调用,线程将永远处于wating状态。
示例
public void waitNotifyLock() throws InterruptedException {
Position position = null;
Thread consumer = new Thread(() -> {
//没有位子进入等待
while (position == null) {
synchronized (this) {
System.out.println("1 进入等待");
try {
//导致当前线程进入等待,加入该对象的等待集合中,并且放弃当前持有的对象锁。
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("3 有位子,吃火锅");
}
});
consumer.start();
Thread.sleep(3000L);
//等位3秒后有位子,
synchronized (this){
this.notify();
//通知顾客有位置了
System.out.println("2 叫号,有位子了,请某某准备就餐");
}
}
park/unpark:
线程调用park则等待 “许可”,unpark方法方法位指定线程提供“许可(permit)”。
不要求park和unpark方法的调用顺序。
该方式对顺序没有要求,但是不会释放synchronize中对象锁。
示例:
public void parkUnparkLock() throws InterruptedException {
Position position = null;
Thread consumer = new Thread(() -> {
//没有位子进入等待
while (position == null) {
synchronized (this) {
System.out.println("1 进入等待");
try {
//导致当前线程进入等待,加入该对象的等待集合中,并且放弃当前持有的对象锁。
LockSupport.park();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("3 有位子,吃火锅");
}
});
consumer.start();
Thread.sleep(3000L);
//等位3秒后有位子,不会释放synchronized中的对象像锁
// synchronized (this){
LockSupport.unpark(consumer);
//通知顾客有位置了
System.out.println("2 叫号,有位子了,请某某准备就餐");
// }
}