join()方法用到了保护性暂停的多线程设计模式
写在前面
虚假唤醒:当前线程还未获得值,但是因为其他线程调用notify()或notifyAll()方法将其唤醒,使其错误的向下执行。
例子:
public class test1 {
static final Object room = new Object();
//两个状态变量
static boolean flag = false;
static boolean tag = false;
public static void main(String[] args) {
//t1线程
new Thread(()->{
synchronized (room){
try {
room.wait();//释放锁并进入等待队列
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(flag){
System.out.println("flag修改成功!");
}else{
System.out.println("flag修改失败!");
}
},"t1").start();
//t2线程
new Thread(()->{
synchronized (room){
try {
room.wait();//释放锁并进入等待队列
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(tag){
System.out.println("tag修改成功!");
}else{
System.out.println("tag修改失败!");
}
},"t2").start();
//t3线程
new Thread(()->{
synchronized (room){
tag=true;//获得锁并修改其中一个属性
room.notify();//唤醒等待队列中的一个线程
}
},"t3").start();
}
}
输出结果:
出现这个原因就是虚假唤醒,值并没有被修改,却将他错误的唤醒了,换成notify()也会有机率使一个线程被错误唤醒,因为你并不知道唤醒的是哪个线程。
解决虚假唤醒的方法:
以ti线程为例,可增加一个判断语句
new Thread(()->{
synchronized (room) {
while (flag == false) {//判断是否修改值,未修改继续等待
try {
room.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if(flag){
System.out.println("flag修改成功!");
}else{
System.out.println("flag修改失败!");
}
},"t1").start();
其实这就是自旋锁的简单使用,满足条件之后才会结束等待
正文
join()方法中就有自旋锁的使用,而且还有保护性暂停的设计模式的使用
保护性暂停保证了线程不会一直处于等待状态
public final synchronized void join(long millis)//millis 位要等待的时间
throws InterruptedException {
long base = System.currentTimeMillis();//获取当前时间
long now = 0; //now为从开始到现在经历的时间
if (millis < 0) { //输入数据有误
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {//如果未设置等待时间则一直等待直到可用
while (isAlive()) {
wait(0);
}
} else {//有设定时间
while (isAlive()) {
long delay = millis - now;//更新需要等待的时间,因为不需要再次等待millis 的时间片
if (delay <= 0) {//如果等待的时间已经达到或者超过设定的时间,则退出
break;
}
wait(delay);//否则继续等待剩余的时间
now = System.currentTimeMillis() - base;//获取已经等待的时间
}
}
}
上述代码的一个小细节:
long delay = millis - now;//更新需要等待的时间,因为不需要再次等待millis 的时间片
if (delay <= 0) {//如果等待的时间已经达到或者超过设定的时间,则退出
break;
}
wait(delay);//否则继续等待剩余的时间
如果没有这个delay变量,那么每次都需要wait() millis时间长度,很显然这有时间的浪费。