写点什么

快速掌握并发编程 --- 线程阻塞与唤醒

用户头像
田维常
关注
发布于: 2020 年 11 月 02 日

关注Java 后端技术全栈”**


回复“000”获取大量电子书


建议先看前面并发编程系列的文章:


快速掌握并发编程---synchronized篇(上)


快速掌握并发编程---synchronized篇(下)


synchronized 结合 java.lang.Object 对象中的wait()notify()notifyAll()



应用


线程 A


public class ThreadA  extends Thread{    private Object lock;    public ThreadA(Object lock) {        this.lock = lock;    }    @Override    public void run() {        synchronized (lock){            System.out.println("ThreadA---start");            try {                //实现线程阻塞                lock.wait();            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println("ThreadA---end");        }    }}
复制代码


线程 B


public class ThreadB extends Thread {    private Object lock;    public ThreadB(Object lock) {        this.lock = lock;    }    @Override    public void run() {        synchronized (lock) {            System.out.println("ThreadB---start");            //实现线程的唤醒            lock.notify();            System.out.println("ThreadB---end");        }    }}
复制代码


测试


public class WaitDemo {    public static void main(String[] args) {        Object lock = new Object();        ThreadA threadA = new ThreadA(lock);        threadA.start();        ThreadB threadB = new ThreadB(lock);        threadB.start();    }}
复制代码


运行输出


ThreadA---startThreadB---startThreadB---endThreadA---end
复制代码


ThreadA 阻塞挂起,ThreadB 唤醒,ThreadA 被唤醒。


上面这段代码我们会发现被阻塞的线程什么时候被唤醒,取决于获得锁的线程什么时候执行完同步代码块并且释放锁。


那怎么做到显示控制呢?我们就需要借 助 一 个 信 号机 制 :在 Object 对 象 中 ,提 供 了 wait/notify/notifyall,可以用于控制线程的状态


wait/notify/notifyAll  基本概念


  • wait:表示持有对象锁的线程 A 准备释放对象锁权限,释放 CPU资源并进入等待状态。

  • notify:表示持有对象锁的线程 A 准备释放对象锁权限,通知 JVM唤醒某个竞争该对象锁的线程 X。线程 A 同步代码块执行结束并且释放了锁之后,线程 X 直接获得对象锁权限,其他竞争线程继续等待(即使线程 X 同步完毕,释放对象锁,其他竞争线程仍然等待,直至有新的 notify ,notifyAll 被调用)。

  • notifyAll:notifyall 和 notify 的区别在于,notifyAll 会唤醒所有竞争同一个对象锁的所有线程,当已经获得锁的线程 A 释放锁之后,所有被唤醒的线程都有可能获得对象锁权限(notifyAll 唤醒等待队列中所有线程,然后所有被唤醒的线程重新进入锁竞争)。


需要注意的是:


三个方法都必须在 synchronized 同步关键字所限定的作用域中调用 ,否则会报错


java.lang.IllegalMonitorStateException


意思是因为没有同步,所以线程对对象锁的状态是不确定的,不能调用这些方法。另外,通过同步机制来确保线程从 wait 方法返回时能够感知到感知到 notify 线程对变量做出的修改 waity /notify 。


waity /notify 的基本原理



注意两个队列:


  • 等待队列:notifyAll/notify 唤醒的就是等待队列中的线程;

  • 同步线程:就是竞争锁的所有线程,等待队列中的线程被唤醒后进入同步队列。


sleep 与 wait 的区别


sleep


  • 让当前线程休眠指定时间。

  • 休眠时间的准确性依赖于系统时钟和 CPU 调度机制。

  • 不释放已获取的锁资源,如果 sleep 方法在同步上下文中调用,那么其他线程是无法进入到当前同步块或者同步方法中的。

  • 可通过调用 interrupt()方法来唤醒休眠线程。

  • sleep 是 Thread 里的方法



wait


  • 让当前线程进入等待状态,当别的其他线程调用 notify()或者 notifyAll()方法时,当前线程进入就绪状态

  • wait 方法必须在同步上下文中调用,例如:同步方法块或者同步方法中,这也就意味着如果你想要调用 wait 方法,前提是必须获取对象上的锁资源

  • 当 wait 方法调用时,当前线程将会释放已获取的对象锁资源,并进入等待队列,其他线程就可以尝试获取对象上的锁资源。

  • wait 是 Object 中的方法




推荐阅读


数组转List,一定要小心这个坑!


【原创】Spring Boot终极篇《上》


【原创】Spring Boot终极篇《下》


发布于: 2020 年 11 月 02 日阅读数: 24
用户头像

田维常

关注

关注公众号:Java后端技术全栈,领500G资料 2020.10.24 加入

关注公众号:Java后端技术全栈,领500G资料

评论

发布
暂无评论
快速掌握并发编程---线程阻塞与唤醒