# wait和notify原理

8 篇文章 0 订阅
6 篇文章 0 订阅

wait和notify原理

wait和notify(重点

小故事

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CDMyQx9F-1627541763521)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210407112231826.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1wwjn2wY-1627541763535)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210407112256160.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9La2jFk3-1627541763537)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210407112303448.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lYwjCpIz-1627541763540)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210407112313985.png)]

wait、notify介绍(必须要获取到锁对象,才能调用这些方法)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RdgVrrIK-1627541763543)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210407112351616.png)]

  • 线程0获取到了,称为 MonitorOwner,但是此时它发现自己要执行 synchronize代码块的条件不满足;此时它就要调用 obj.wait方法,进入到Monitor中的 waitSet集合,此时 线程0的状态就变为WAITING
  • 处于BLOCKEDWAITING状态的线程都为 阻塞状态,CPU都不会分给他们时间片。但是有所区别:
    • BLOCKED状态的线程实在竞争锁对象时,发现Monitor的Owner已经是别的线程了此时就会进入EntryList中,并处于BLOCKED状态
    • WAITING状态的线程是获取了对象的锁,但是自身的原因无法执行synchronize的临界区资源需要进入阻塞状态时,锁对象调用了wait方法而进入了WaitSet中,处于WAITING状态
    • ·处于BLOCKED状态的线程会在锁被释放的时候被唤醒
    • 处于WAITING扎UN盖亚IDE线程只有被锁对象调用了nofity方法(obj.notify/obj.nofityAll),才会被唤醒。然后它会进入到EntryList,重新竞争锁(此时就会将锁升级为 重量级锁)

PS:

可以参考生产者/消费者模式来进行理解。有如下代码:

package socket;

import java.util.Random;

//生产消费者
public class Test7 {
    public static void main(String[] args) {
        Box box=new Box();
        Thread t1=new Thread(new Producer(box));
        t1.setName("生产者");
        Thread t2=new Thread(new Consumer(box));
        t2.setName("消费者");
        t1.start();
        t2.start();
    }
}

//生产任务类
class Producer implements Runnable{
    Box box;

    Producer(Box box){
        this.box=box;
    }

    @Override
    public void run() {
        Random r=new Random();
        for (int i = 0; i < 10; i++) {
            Apple a=new Apple(i);
            box.deposite(a);
            try {
                Thread.sleep(r.nextInt(3)*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//消费任务类
class Consumer implements Runnable{
    Box box;//生产和消费操作的资源相同

    Consumer(Box box){
        this.box=box;
    }

    @Override
    public void run() {
        Random r=new Random();
        for (int i = 0; i < 10; i++) {
            Apple a=box.withdraw();
            try {
                Thread.sleep(r.nextInt(3)*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//容器,存放苹果
class Box{
    Apple [] apples=new Apple[5];
    int index=0;//容器索引

    //入库
    public synchronized void deposite(Apple apple){
        while(index>=apples.length){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notifyAll();//唤醒其他线程
        apples[index]=apple;//入库
        index++;
        System.out.println(Thread.currentThread().getName()+"生产了"+apple);
    }

    //出库
    public synchronized Apple withdraw(){
        while (index==0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notifyAll();
        Apple a=apples[index-1];
        index--;
        System.out.println(Thread.currentThread().getName()+"消费了"+a);
        return a;
    }
}

//苹果类
class Apple{
    int id;
    Apple(int id){
        this.id=id;
    }

    @Override
    public String toString() {
        return "Apple{" +
                "id=" + id +
                '}';
    }
}

API介绍

下面的三个方法都是 Object中的方法;通过 锁对象来调用

  • wait():让获得锁对象的线程到 waitSet中一直等待
  • wait(long n ):让该等待线程没有被notify,等到时间到了之后,也会自动唤醒
  • notify():让获得对象所的线程,使用锁对象调用notify去waitSet的等待线程中挑一个唤醒
  • notifyAll():让获得对象锁的线程,使用锁对象调用notifyAll去唤醒waitSet中所有的等待线程

它们都是 线程之间进行协作的手段,都属于Object对象的方法,必须要获得此对象的锁,才能调用这些方法

注:只有当对象被锁以后(称为Owner),才能调用wait和notify方法

public class Test1 {
    final static Object LOCK = new Object();
    public static void main(String[] args) throws InterruptedException {
        //只有在对象被锁住后才能调用wait方法
        synchronized (LOCK) {
            LOCK.wait();
        }
    }
}
  • 演示wait和notify方法
  /**
 * Description:
 *
 * @author HillCheung
 * @date 2021年4月7日11:36:55
 */

@Slf4j(topic = "guizy.WaitNotifyTest")
    public class WaitNotifyTest {
        static final Object obj = new Object();

        public static void main(String[] args) throws Exception {

            new Thread(() -> {
                synchronized (obj) {
                    log.debug("执行...");
                    try {
                        // 只有获得锁对象之后, 才能调用wait/notify
                        obj.wait(); // 此时t1线程进入WaitSet等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.debug("其它代码...");
                }
            }, "t1").start();

            new Thread(() -> {
                synchronized (obj) {
                    log.debug("执行...");
                    try {
                        obj.wait(); // 此时t2线程进入WaitSet等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.debug("其它代码...");
                }
            }, "t2").start();

            // 让主线程等两秒在执行,为了`唤醒`,不睡的话,那两个线程还没进入waitSet,主线程就开始唤醒了
            Thread.sleep(1000);
            log.debug("唤醒waitSet中的线程!");
            // 只有获得锁对象之后, 才能调用wait/notify
            synchronized (obj) {
                // obj.notify(); // 唤醒waitset中的一个线程
                obj.notifyAll(); // 唤醒waitset中的全部等待线程
            }
        }
    }

13:01:36.176 guizy.WaitNotifyTest [t1] - 执行...
            13:01:36.178 guizy.WaitNotifyTest [t2] - 执行...
            13:01:37.175 guizy.WaitNotifyTest [main] - 唤醒waitSet中的线程!
            13:01:37.175 guizy.WaitNotifyTest [t2] - 其它代码...
            13:01:37.175 guizy.WaitNotifyTest [t1] - 其它代码...

Sleep(long n) 和 Wait(long n)的区别 (重点)

不同点

  • Sleep是Thread类的静态方法Wait是Object方法 ,Object又是所有类的父类,所以所有类都有Wait方法。
  • Sleep在阻塞的时候不会释放锁,而Wait在阻塞的时候回释放锁(不释放锁的话,其他线程就无法唤醒线程了)
  • Sleep方法不需要与synchronize一起使用,而Wait方法需要与synchronize一起使用(waiy/notify等方法,必须要使用对象所来调用)

相同点:

  • 阻塞状态都为TIME_WAITING(显示等待)

sleep方法 / wait方法 测试

/**
 * Description: 测试sleep不释放锁
 *
 * @author HillCheung
 * @date 2020/12/20 09:33
 */
@Slf4j(topic = "guizy.SleepTest")
public class SleepTest {

    public static final Object lock = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (lock) {
                log.debug("获得锁了");
                try {
                    // Thread.sleep(5000); // 主线程需要等5s才能获得到锁.所以所在sleep期间, 是不会释放锁的
                    lock.wait(5000); // 调用wait方法会立刻释放锁, 不然主线程就拿不到lock锁了, 当等待5s后程序才结束
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1").start();

        // 主线程睡一秒
        Sleeper.sleep(1);
        synchronized (lock) {
            log.debug("获得锁了");
        }
    }

sleep打印结果 : 表明在sleep期间, 锁是不会被释放的

在这里插入图片描述

wait打印结果 : 当调用wait方法后, 锁就会被立刻释放

在这里插入图片描述

wait/notify的使用

Step 1 : 逐渐向下优化

@Slf4j(topic = "guizy.WaitNotifyTest")
public class WaitNotifyTest {
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout = false;
public static void main(String[] args) {
    //思考下面的解决方案好不好,为什么?
    new Thread(() -> {
        synchronized (room) {
            log.debug("有烟没?[{}]", hasCigarette);
            if (!hasCigarette) {
                log.debug("没烟,先歇会!");
                Sleeper.sleep(2);   // 会阻塞2s, 不会释放锁
            }
            log.debug("有烟没?[{}]", hasCigarette);
            if (hasCigarette) {
                log.debug("可以开始干活了");
            }
        }
    }, "小南").start();

    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            synchronized (room) {
                log.debug("可以开始干活了");
            }
        }, "其它人").start();
    }

    Sleeper.sleep(1);
    new Thread(() -> {
        // 此时没有加锁, 所以会优先于其他人先执行
        // 这里能不能加 synchronized (room)?
        //synchronized (room) { // 如果加锁的话, 送烟人也需要等待小南睡2s的时间,此时即使送到了,小南线程也将锁释放了..
            hasCigarette = true;
            log.debug("烟到了噢!");
        //}
    }, "送烟的").start();
}
}
  • 不给送烟线程加synchronized输出情况:
DEBUG [小南] - 有烟没?[false]
DEBUG [小南] - 没烟,先歇会!
DEBUG [送烟的] - 烟到了噢!
DEBUG [小南] - 有烟没?[true]
DEBUG [小南] - 可以开始干活了
DEBUG [其它人] - 可以开始干活了
DEBUG [其它人] - 可以开始干活了
DEBUG [其它人] - 可以开始干活了
DEBUG [其它人] - 可以开始干活了

在这里插入图片描述

Step2:

@Slf4j(topic = "guizy.WaitNotifyTest")
public class WaitNotifyTest2 {
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            synchronized (room) {
                log.debug("有烟没?[{}]", hasCigarette);
                if (!hasCigarette) {
                    log.debug("没烟,先歇会!");
                    try {
                        room.wait(); // 此时进入到waitset等待集合, 同时会释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (hasCigarette) {
                    log.debug("可以开始干活了");
                }
            }
        }, "小南").start();

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                // 小南进入等待状态了, 其他线程就可以获得锁了
                synchronized (room) {
                    log.debug("可以开始干活了");
                }
            }, "其它人").start();
        }

        Thread.sleep(1);
        new Thread(() -> {
            synchronized (room) {
                hasCigarette = true;
                log.debug("烟到了噢!");
                room.notify();
            }
        }, "送烟的").start();
    }
}

运行结果如下:

11:00:51.840 guizy.WaitNotifyTest [小南] - 有烟没?[false]
        11:00:51.847 guizy.WaitNotifyTest [小南] - 没烟,先歇会!
        11:00:51.847 guizy.WaitNotifyTest [其它人] - 可以开始干活了
        11:00:51.847 guizy.WaitNotifyTest [其它人] - 可以开始干活了
        11:00:51.847 guizy.WaitNotifyTest [其它人] - 可以开始干活了
        11:00:51.847 guizy.WaitNotifyTest [其它人] - 可以开始干活了
        11:00:51.847 guizy.WaitNotifyTest [其它人] - 可以开始干活了
        11:00:52.847 guizy.WaitNotifyTest [送烟的] - 烟到了噢!
        11:00:52.847 guizy.WaitNotifyTest [小南] - 有烟没?[true]
        11:00:52.848 guizy.WaitNotifyTest [小南] - 可以开始干活了

如果此时除了小南在等待唤醒, 还有一个线程也在等待唤醒呢? 此时的notify方法会唤醒谁呢?

在这里插入图片描述

Step3:

@Slf4j(topic = "guizy.WaitNotifyTest")
public class WaitNotifyTest3 {
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            synchronized (room) {
                log.debug("有烟没?[{}]", hasCigarette);
                if (!hasCigarette) {
                    log.debug("没烟,先歇会!");
                    try {
                        room.wait(); // 此时进入到waitset等待集合, 同时会释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("有烟没?[{}]", hasCigarette);
                if (hasCigarette) {
                    log.debug("可以开始干活了");
                }
            }
        }, "小南").start();

        new Thread(() -> {
            synchronized (room) {
                log.debug("外卖送到没?[{}]", hasTakeout);
                if (!hasTakeout) {
                    log.debug("没外卖,先歇会!");
                    try {
                        room.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("外卖送到没?[{}]", hasTakeout);
                if (hasTakeout) {
                    log.debug("可以开始干活了");
                } else {
                    log.debug("没干成活...");
                }
            }
        }, "小女").start();

        Thread.sleep(1);
        new Thread(() -> {
            synchronized (room) {
                hasTakeout = true;
                log.debug("外卖到了噢!");
                room.notify();
            }
        }, "送外卖的").start();
    }
}

运行结果如下:

DEBUG [小南] - 有烟没?[false]
        DEBUG [小南] - 没烟,先歇会!
        DEBUG [送外卖的] - 外卖到了噢!
        DEBUG [小女] - 外卖送到没?[true]
        DEBUG [小女] - 外卖送到没?[true]
        DEBUG [小女] - 可以开始干活了
        DEBUG [小南] - 有烟没?[false]

问题: 当外卖送到了, 却唤醒了小南, 此时就出现了问题

在这里插入图片描述

new Thread(() -> {
synchronized (room) {
        hasTakeout = true;
        log.debug("外卖到了噢!");
        room.notifyAll();
        }
        }, "送外卖的").start();

运行结果如下:

DEBUG [小南] - 有烟没?[false]
        DEBUG [小南] - 没烟,先歇会!
        DEBUG [送外卖的] - 外卖到了噢!
        DEBUG [小女] - 外卖送到没?[true]
        DEBUG [小女] - 外卖送到没?[true]
        DEBUG [小女] - 可以开始干活了
        DEBUG [小南] - 有烟没?[false]

在这里插入图片描述

Step5:

  • 使用while循环来解决虚假唤醒
@Slf4j(topic = "guizy.WaitNotifyTest")
public class Main {
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            synchronized (room) {
                log.debug("有烟没?[{}]", hasCigarette);
                while (!hasCigarette) {
                    log.debug("没烟,先歇会!");
                    try {
                        room.wait(); // 此时进入到waitset等待集合, 同时会释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("有烟没?[{}]", hasCigarette);
                if (hasCigarette) {
                    log.debug("可以开始干活了");
                }
            }
        }, "小南").start();

        new Thread(() -> {
            synchronized (room) {
                log.debug("外卖送到没?[{}]", hasTakeout);
                while (!hasTakeout) {
                    log.debug("没外卖,先歇会!");
                    try {
                        room.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("外卖送到没?[{}]", hasTakeout);
                if (hasTakeout) {
                    log.debug("可以开始干活了");
                } else {
                    log.debug("没干成活...");
                }
            }
        }, "小女").start();

        Thread.sleep(1);
        new Thread(() -> {
            synchronized (room) {
                hasTakeout = true;
                log.debug("外卖到了噢!");
                room.notifyAll();
            }
        }, "送外卖的").start();
    }
}

运行结果如下:

DEBUG [小南] - 有烟没?[false]
        DEBUG [小南] - 没烟,先歇会!
        DEBUG [送外卖的] - 外卖到了噢!
        DEBUG [小女] - 外卖送到没?[true]
        DEBUG [小女] - 外卖送到没?[true]
        DEBUG [小女] - 可以开始干活了
        DEBUG [小南] - 没烟,先歇会!

因为改为while如果唤醒之后,就在while循环中执行了,不会跑到while外面去执行"'有烟没…",此时小南就不需要每次notify,就去看是不是送来的烟,如果是烟,while就变为false了.


同步模式之保护性暂停(join、Future的实现

  • Guarded Suspension,用在一个线程等待另外一个线程的执行结果
    • 一个结果需要从一个线程传递到另一个线程,让他们 关联同一个GuardedObject
    • 如果 有结果不断从一个线程到另一个线程那么可以使用 消息队列(见生产者/消费者)
    • JDK中, join的实现、Future的实现,采用的就是此模式
    • 因为要等待另一方的结构,因此归类到同步模式

1594473284105

  • 一方等待另一方的执行结果举例 :

  • 举例, 线程1等待线程2下载的结果,并获取该结果

@Slf4j(topic = "GuardeObjectTest")
public class GuardeObjectTest {
    public static void main(String[] args) {
        GuardObject guardObject =new GuardObject();
        new Thread(()->{
           log.debug("等待结果");
            List<String> list = (List<String>) guardObject.get();
            log.debug("结果大小:{}",list.size());
        },"t1").start();


        new Thread(()->{
            log.debug("执行下载");
            List<String> list =new ArrayList<>();;
            list.add("1");
            list.add("2");
            guardObject.complete(list);
        },"t2").start();
    }


}
class  GuardObject{
    //结果
    private Object response;

    //获取结果
    @SneakyThrows
    public Object get(){
        synchronized (this){
            //防止虚假唤醒
            //没有结果
            while(response==null){
                this.wait();
            }
        }
        return response;
    }


    //产生结果
    public void complete(Object response){
        synchronized (this){
            //给变量赋值
            this.response=response;
            this.notifyAll();
        }
    }
}

结果如下:

DEBUG [t1] - 等待结果
        DEBUG [t2] - 执行下载
        DEBUG [t1] - 结果大小:2
  • 线程t1等到线程t2的结构,可以设置超时时间,如果超过时间还没有返回结构,此时就不等了,退出while循环。

运行结果

// 在等待时间内的情况
16:20:41.627 guizy.GuardeObjectTest [t1] - begin
        16:20:41.627 guizy.GuardeObjectTest [t2] - begin
        16:20:42.633 guizy.GuardeObjectTest [t1] - 结果是:java.lang.Object@1e1d0168

// 超时的情况
        16:21:24.663 guizy.GuardeObjectTest [t2] - begin
        16:21:24.663 guizy.GuardeObjectTest [t1] - begin
        16:21:26.667 guizy.GuardeObjectTest [t1] - 结果是:null
  • 关于超时的增强,在join(long millis) 的源码中得到了体现:
public final synchronized void join(long millis)
        throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        // join一个指定的时间
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}
  • 多任务版 GuardedObject图中Futures就好比居民楼一楼的信箱(每个信箱有房间编号),左侧的t0,t2,t4就好比等待右键的居民,右侧的t1,t3,t5就好比邮递员如果需要多个类之间使用GuardedObject对象,作为参数传递不是很方便,因此设计一个解耦的中间类
  • 不仅能够解耦【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理。和生产者消费者模式的区别就是 :这个 产生结构的线程使用结构的线程一一对应的关系,但是生产者消费者模式并不是
  • rpc框架的调用中就使用到了这种模式。

1594518049426

运行结果如下:

DEBUG [Thread-1] - 开始收信 id:1
        DEBUG [Thread-3] - 送信 id:3, 内容:内容3
        DEBUG [Thread-5] - 送信 id:1, 内容:内容1
        DEBUG [Thread-4] - 送信 id:2, 内容:内容2
        DEBUG [Thread-0] - 开始收信 id:2
        DEBUG [Thread-2] - 开始收信 id:3
        DEBUG [Thread-2] - 收到信 id:3, 内容:内容3
        DEBUG [Thread-0] - 收到信 id:2, 内容:内容2
        DEBUG [Thread-1] - 收到信 id:1, 内容:内容1

        Process finished with exit code 0

异步模式之生产者/消费者(重点

  • 与前面的 保护性暂停中的GuardedObject不同,不需要产生结构消费结构线程一一对应(一个生产一个消费)
  • 消费队列可以用来 平衡生产和消费的线程资源
  • 生产者仅负责产生结果数据,不关心数据该如何处理,而消费者专心处理结果数据
  • 消息队列是 有容器限制的,满时不会再加入数据,空时不会消耗数据
  • JDK中各种 阻塞队列,采用的就是这种模式

异步模式中, 生产者产生消息之后消息没有被立刻消费
同步模式中, 消息在产生之后被立刻消费了。

1594524622020

  • 下面例子是线程间通信消息队列,要注意区别,像RabbitMQ等消息框架是进程间通信的。
DEBUG [生产者0] - 已生产消息Message{id=0, value=0}
DEBUG [消费者] - 已消费消息Message{id=0, value=0}
DEBUG [生产者1] - 已生产消息Message{id=1, value=1}
DEBUG [生产者2] - 已生产消息Message{id=2, value=2}
DEBUG [消费者] - 已消费消息Message{id=1, value=1}
DEBUG [消费者] - 已消费消息Message{id=2, value=2}
DEBUG [消费者] - 队列为空,消费者线程等待

prak&unpack(重要)

基本使用

  • park/unpark都是LockSupport类中的方法

  • 先调用unpark后,在调用park,此时park不会暂停线程

 // 暂停当前线程
        LockSupport.park();
// 恢复某个线程的运行
        LockSupport.unpark(thread);

原理

每个线程都要自己的一个Parker对象,由三部分组成_counter,_cond和_mutex

  • 打个比喻线程就像一个旅人,Parker就像他随身携带的背包,条件变量_cond就好比背包的 , _counter就好比背包中的备用干粮(0位耗尽,1位充足)
  • 调用park就是要看需不需要停下来歇息
    • 如果备用干粮耗尽,那么钻进帐篷休息
    • 如果备用干粮充足,那么不许停留,继续前进。
  • 调用unpark,就好比令干粮充足
    • 如果这时线程就在帐篷,就唤醒让他继续前进
    • 如果这时线程还在运行,那么下次他调用park时,仅是消耗掉备用干粮,不许停留继续前进
    • 因为背包空间有限,多次调用unpark仅会补充一份备用干粮

先调用park再调用unpark的过程

  • 先调用park的情况
    • 当前线程调用 Unsafe.park()方法
    • 检查_counter,是否为0,如果为0,获得_mutex互斥锁(mutex对象有一个等待对了_cond)
    • 线程进入_cond条件变量阻塞
    • 设置_counter=0(没干粮了)

1594531894163

  • 调用unpark

    • 调用Unsafe.unpark(Thread_0)方法,设置_count为1
    • 唤醒_cond条件变量中的Thread _0
    • Thread_0恢复运行
    • 设置_counter为0

1594532057205

先调用unpark再调用park的过程

  • 调用Unsafe.unparh(Thread_0)方法,设置_count为1
  • 当前线程调用Unsafe.park()方法
  • 检查_counter,本情况为1,这时候线程无需阻塞,继续运行
  • 设置_counter为0

1594532135616

线程状态转换(重点)

img

在这里插入图片描述

假设有线程Threa t

  1. New<->Runnable

    • t.start()方法时new--->runnable
  2. Runnable<->Waiting

    • 线程用synchronize(obj)获取了对象所
      • 调用obj.wait()方法时,t线程进入了waitSet中,从Runnable->waiting
      • 调用obj.nofty()、obj.notifyAll()、t.interrupt()时,唤醒的线程都到 entrySet阻塞队列成为BLOCKED状态,在阻塞队列,和其他线程在进行竞争锁
        • **竞争锁成功,**t线程从waiting->runnable
        • 竞争锁失败,t线程从waiting->blocked
  3. runnable->waiting

    • 当前线程调用t.join()方法时,当前线程runnable->waiting,注意是 当前线程在t线程对象在 waitSet上等待
    • t线程运行结束,或调用了当前线程的interrupt()时,当前线程从waiting->runnable
  4. RUNNABLE <–> WAITING

    • 当前线程调用 LockSupport.park() 方法会让当前线程RUNNABLE --> WAITING
    • 调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ,会让目标线程从 WAITING --> RUNNABLE

    Runnable 和 Timed-Waiting的相互转换

  5. RUNNABLE <–> TIMED_WAITING (带超时时间的wait)

    • t 线程用synchronized(obj) 获取了对象锁
      • 调用 obj.wait(long n) 方法时,t 线程从 RUNNABLE --> TIMED_WAITING
      • 线程等待时间超过了 n 毫秒,或调用 obj.notify() , obj.notifyAll() , t.interrupt() 时; 唤醒的线程都到entrySet阻塞队列成为BLOCKED状态, 在阻塞队列,和其他线程再进行 竞争锁
        • 竞争锁成功,t 线程从 TIMED_WAITING --> RUNNABLE
        • 竞争锁失败,t 线程从 TIMED_WAITING --> BLOCKED
  6. RUNNABLE <–> TIMED_WAITING

    • 当前线程调用 t.join(long n) 方法时,当前线程从 RUNNABLE --> TIMED_WAITING 注意是当前线程在t 线程对象的waitSet等待
    • 当前线程等待时间超过了 n 毫秒,或t 线程运行结束,或调用了当前线程的 interrupt() 时,当前线程从 TIMED_WAITING --> RUNNABLE
  7. RUNNABLE <–> TIMED_WAITING

    • 当前线程调用 Thread.sleep(long n) ,当前线程从 RUNNABLE --> TIMED_WAITING
    • 当前线程等待时间超过了 n 毫秒或调用了线程的 interrupt() ,当前线程从 TIMED_WAITING --> RUNNABLE
  8. RUNNABLE <–> TIMED_WAITING

    • 当前线程调用 LockSupport.parkNanos(long nanos) 或 LockSupport.parkUntil(long millis) 时,当前线程从 RUNNABLE --> TIMED_WAITING
    • 调用LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ,或是等待超时,会让目标线程从 TIMED_WAITING--> RUNNABLE
  9. RUNNABLE <–> BLOCKED

    • t 线程用 synchronized(obj) 获取了对象锁时如果竞争失败,从 RUNNABLE –> BLOCKED, 持 obj 锁线程的同步代码块执行完毕,会唤醒该对象上所有 BLOCKED 的线程重新竞争,如果其中 t 线程竞争 成功,从 BLOCKED –> RUNNABLE ,其它失败的线程仍然 BLOCKED
  10. RUNNABLE <–> TERMINATED

    • 当前线程所有代码运行完毕,进入 TERMINATED
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值