wait和notify
wait()方法
wait()执行后,当前线程出让CPU,释放锁,此时当前线程不再继续往下执行。从监视资源的线程中随机选一个继续执行。上一个wait的线程想要继续执行,需要等到其他线程把它唤醒,并且释放锁之后才能继续执行。
// 方法注释中写的,需要这么用
synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
}
notify()和notifyAll()方法
和wait()方法配合使用,表示当前线程即将释放锁,通知jvm可以唤醒其他正在等待状态的线程。必须在synchronized同步的场景下使用,否则会抛出IllegalMonitorStateException异常。
举个栗子:
public class NotifyTest extends Thread{
private int number = 6;
public byte res[];
public NotifyTest(int number, byte a[]){
this.number = number;
res = a;
}
@Override
public void run(){
System.out.println(Thread.currentThread().getName() + "进入方法");
synchronized (res){
while(number -- > 0){
System.out.println(Thread.currentThread().getName() + "进入循环");
try {
res.notify();// 这里表示立即唤醒等待的线程
String name = Thread.currentThread().getName();
// 因为t1后者t3进来之后总是wait,需要其他线程唤醒它,所以例子可能会因为某个线程wait无法停止
if("t1".equals(name) || "t3".equals(name)){
System.out.println("我是"+name+"线程,不继续执行了");
res.wait();
}else {
for (int i = 0; i < 3; i++) {
try {
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// res.notifyAll(); 表示唤醒当前线程即将释放锁,让jvm唤醒所有竞争这个锁的线程
}
if("t1".equals(name) || "t3".equals(name)){
System.out.println("我是"+name+"线程,我又能执行了");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
final byte a[] = {0};//以该对象为共享资源
Thread t1 = new Thread(new NotifyTest((2),a),"t1");
Thread t2 = new Thread(new NotifyTest((2),a),"t2");
Thread t3 = new Thread(new NotifyTest((2),a),"t3");
t1.start();
t3.start();
t2.start();
}
}
一次运行结果:
/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ com.tomas.thread.stu01.NotifyTest
t3进入方法 // t3率先进入方法获得资源锁
t2进入方法 // t2进入等待
t1进入方法 // t1进入等待
t3进入循环 // t3拥有资源锁可以继续执行
我是t3线程,不继续执行了 // 执行到wait而暂停执行,释放锁,让t1和t2去竞争
t1进入循环 // t1竞争到资源继续执行
我是t1线程,不继续执行了 // 在这之前,t1执行了notify,此时t3已经被唤醒了,t1释放锁
t2进入循环 // t2和t3竞争,t2成功获得资源
0
1
2
t2进入循环
0
1
2 // t2执行的时候会执行notify,此时t1已经被唤醒了,t2此时执行结束
我是t1线程,我又能执行了 // t1和t3竞争,t1继续执行
t1进入循环
我是t1线程,不继续执行了
我是t3线程,我又能执行了
t3进入循环
我是t3线程,不继续执行了
我是t1线程,我又能执行了 // 最终t3还在等待
"t3" #16 prio=5 os_prio=31 tid=0x00007fd554071800 nid=0xa903 in Object.wait() [0x00007000101de000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076ac27350> (a [B)
at java.lang.Object.wait(Object.java:502)
at com.tomas.thread.stu01.NotifyTest.run(NotifyTest.java:29)
- locked <0x000000076ac27350> (a [B)
at java.lang.Thread.run(Thread.java:748)
总结
使用obj作为共享资源。在同步obj对象的场景下使用。当前线程在执行obj.wait()之后出让cup使用权限,当前线程停止执行,释放锁资源。这时需要其他线程执行obj.notify()方法唤醒wait的线程。等其他线程执行完或者释放锁之后,等待线程继续往下执行。
interrupt和sleep
interrupt()方法
使用interrupted信号量实现线程之间的通信。线程外部向线程内部发出一个中断请求的时候,线程内部会有一个响应,但是线程并不会中断,线程中断的控制权在自己手中。此时被中断线程可以选择继续执行。
sleep()方法
sleep方法执行之后,线程不会释放锁资源。正在sleep的线程被外部请求中断后,会抛出InterruptedException
异常,此时interrupted信号量被清除,即线程继续执行。
换句话说,信号量为true的线程往下执行,遇到sleep,wait这样的方法时,会会抛出中断异常,然后interrupted被复位。
Thread.interrupted()
中断信号量复位。
t1.interrupt()
中断方法,设置t1线程的中断状态为true。
举个栗子:
public class InterruptTest extends Thread {
public static void main(String[] args) {
Thread t1 = new Thread(new InterruptTest());
Thread t2 = new Thread(new InterruptTest());
t1.start();
//t2.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//t1.interrupt();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "进入方法");
synchronized (InterruptTest.class) {
System.out.println("中断标志第一次打印" + currentThread().isInterrupted());
currentThread().interrupt(); // 中断自己,设置为true
System.out.println("中断标志第二次打印" + currentThread().isInterrupted());
try {
System.out.println(Thread.currentThread().getName() + "暂停一下");
TimeUnit.SECONDS.sleep(10);
System.out.println("------------这里肯定不会执行,因为走到异常了");
} catch (InterruptedException e) {
System.out.println("中断标志第三次打印" + currentThread().isInterrupted());
System.out.println("模拟一下异常流程处理");
}
System.out.println("我还没结束,继续打印");
// 再中断一下自己
Thread.currentThread().interrupt();
System.out.println("中断标志第四次打印" + currentThread().isInterrupted());
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
System.out.println("会进异常");
System.out.println("中断标志第五次打印" + currentThread().isInterrupted());
System.out.println(Thread.currentThread().getName() + "==" + e.getMessage());
}
System.out.println("不浪了,执行最后一句,退出run方法");
}
}
}
执行结果:
Thread-1进入方法
中断标志第一次打印false
中断标志第二次打印true
Thread-1暂停一下
中断标志第三次打印false
模拟一下异常流程处理
我还没结束,继续打印
中断标志第四次打印true
会进异常
中断标志第五次打印false
Thread-1==sleep interrupted
不浪了,执行最后一句,退出run方法
总结:
interrupt为true的线程执行过程中遇到sleep方法总会抛出中断异常,然后复位信号量为false。本例验证了interrupt=true的线程执行sleep时会抛中断异常,也可以让线程先sleep,从外部调用中断方法进行中断,也会抛中断异常。可以认为中断标志为true的线程执行sleep会进异常流程;正在sleep的线程被外部执行中断方法,也会进入异常流程。相同的是,进入异常之后,中断状态会被清空,设置为false,如果此时是用中断状态控制流程处理,则会继续执行,就和没有中断过一样。
问题
-
如何停掉一个线程?
(1)stop方法是过期的,为什么不推荐使用。这种暴力停止的方式会导致数据丢失。因此,线程的中断应该交给被中断的线程自己去决定要不要中断。
(2)使用thread.interrupt()方法。jvm底层通过信号量实现。当一个线程正在wait或者sleep的时候去中断,线程就会响应一个中断异常。此时线程并没有停止,此时线程可以继续往下执行。
(3)使用Thread.interrupted()方法。这个方法是重制信号量。
-
Java线程有几种状态?操作系统有几种?
Java有六种状态。
new 、runnable 、waiting、timed_waiting、blocked、terminated
操作系统有五种状态:
就绪、阻塞、等待、运行、完成
-
线程的生命周期和触发机制
线程的生命周期就指线程从创建到销毁,这个过程中会经历多种状态,状态转换的条件就是触发机制。
-
线程状态的转换