简介
在实际多线程运用时,我们可能需要多个线程之间同步运行,如线程1线运行完,线程2再运行
线程固定顺序运行
- 两个线程保证:
- 2线程先打印b
- 1线程再打印a
wait/notify实现
设置一个Boolean变量,如果为假,让其中一个线程自旋阻塞
/**
* 两个线程保证:
* 2线程先打印b
* 1线程再打印a
*
* wait/notify实现
*/
public class TwoThread {
static final Object lock = new Object();
static boolean t2Run = false;
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (lock){
while(!t2Run){
//此处不可为if(!t2Run),否则会出现假唤醒的情况,即唤醒后会在执行完lock.wait()停止了,不会执行下面的代码
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+":a");
}
},"t1");
Thread t2 = new Thread(() -> {
synchronized (lock){
System.out.println(Thread.currentThread().getName()+":b");
t2Run = true;
lock.notify();
}
},"t2");
t1.start();
t2.start();
}
}
park/unpark实现(简单)
-
LockSupport就是通过控制变量_counter来对线程阻塞唤醒进行控制的。原理有点类似于信号量机制。
-
当调用park()方法时,会将_counter置为0,同时判断前值,等于1说明前面被unpark过,则直接退出,否则将使该线程阻塞。
-
当调用unpark()方法时,会将_counter置为1,同时判断前值,小于1会进行线程唤醒,否则直接退出。
-
形象的理解,线程阻塞需要消耗凭证(permit),这个凭证最多只有1个。当调用park方法时,如果有凭证,则会直接消耗掉这个凭证然后正常退出;
-
但是如果没有凭证,就必须阻塞等待凭证可用;而unpark则相反,它会增加一个凭证,但凭证最多只能有1个。
-
为什么可以先唤醒线程后阻塞线程?
-
因为unpark获得了一个凭证,之后调用park因为有凭证消费,故不会阻塞。
-
为什么唤醒两次后阻塞两次会阻塞线程。
-
因为凭证的数量最多为1,连续调用两次unpark和调用一次unpark效果一样,只会增加一个凭证;而调用两次park却需要消费两个凭证。
public class TwoThredP2 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
LockSupport.park();
System.out.println(Thread.currentThread().getName() + ":a");
}, "t1");
Thread t2 =