• 线程基础知识08 线程的等待和唤醒


    1 线程等待和唤醒简介
    2 示例:synchronized + Object的wait()和notify()方法
      2.1 代码
      2.2 执行结果
      2.3 注意事项
    3 示例 Lock的lock()方法和unlock()方法+await()和signal()方法
      3.1 代码
      3.2 执行结果
      3.3 注意事项
    4 LockSupport的park()和unpark()方法
      4.1 代码
      4.2 执行结果
      4.3 注意事项
    5 几种等待的特点及区别
    6 示例证明LockSupport不会释放锁
    7 示例(证明object.wait会释放锁)
    8 线程状态迁移图    

    ---------------------------------------------------------------------------------------------------------

    1 简介

      目前,JAVA提供了三种线程等待唤醒的机制。

      1)synchronized + Object的wait()和notify()方法

      2)Lock的lock()方法和unlock()方法+await()和signal()方法

      3)LockSupport的park()和unpark()方法

    2 示例:synchronized + Object的wait()和notify()方法

    2.1 代码

    public class LockTest1 {
    
        private static Object lo = new Object();
    
        public static void main(String[] args) {
    
    
            Thread t1 = new Thread(()->{
                synchronized (lo){
                    System.out.println("aaaaaaaaaaa1");
                    try {
                        lo.wait(); //让出锁,并等待notice
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("aaaaaaaaaaa2");
                }
    
            });
    
            Thread t2 = new Thread(()->{
                try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
                synchronized (lo){
                    System.out.println("bbbbbbbbb1");
                    try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
                    System.out.println("bbbbbbbbb2");
                    lo.notify(); //唤醒t1线程
                    try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
                    System.out.println("bbbbbbbbb3");
                }
    
            });
            t1.start();
            t2.start();
    
        }
    }

    2.2 执行结果

      t1线程线获得锁,然后调用wait方法让出锁,并等待。t2线程获取锁,然后调用notify()方法唤醒t1线程(注意,t2没有让出锁),等待t2执行完,t1获取锁继续执行

    aaaaaaaaaaa1
    bbbbbbbbb1
    bbbbbbbbb2
    bbbbbbbbb3
    aaaaaaaaaaa2

    2.3 注意事项

      1)wait和notice方法的调用必须在synchronize里面

      2)wait和notice方法一一对应

      3)wait方法需要先于notice方法执行

    3 示例 Lock的lock()方法和unlock()方法+await()和signal()方法

    3.1 代码

    public class LockTest3 {
        
        static Lock lock = new ReentrantLock();
    
        static Condition condition = lock.newCondition();
    
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(()->{
                System.out.println("aaaaaaaa1");
                try {
                    lock.lock();
                    Thread.sleep(2000);
                    System.out.println("aaaaaaa2");
                    condition.await(); //让出锁,并等待signal
                }catch (Exception e){
                }finally {
                    lock.unlock();
                    System.out.println("aaaaaaa3");
                }
            });
    
            Thread t2 = new Thread(()->{
                try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
                System.out.println("bbbbbbbb1");
                try {
                    lock.lock();
                    System.out.println("bbbbbbbbb2");
                    Thread.sleep(2000);
                    condition.signal();
                }catch (Exception e){
                }finally {
                    lock.unlock();
                    try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
                    System.out.println("bbbbbbbbb3");
                }
            });
            t1.start();
            t2.start();
        }
    }

    3.2 执行结果

      t1先调用lock方法,获取锁,然后调用await()方法让出锁,并等待。t2调用lock方法获取锁,调用signal()方法唤醒t1线程(t2没有让出锁),t2调用unlock方法让出锁,t1获取锁继续执行

    aaaaaaaa1
    bbbbbbbb1
    aaaaaaa2
    bbbbbbbbb2
    aaaaaaa3
    bbbbbbbbb3

    3.3 注意事项

      1)await和signal方法的调用必须在lock和unlock之间

      2)await和signal方法一一对应

      3)await方法需要先于signal方法执行

    4 LockSupport的park()和unpark()方法

    4.1 代码

    public class LockTest4 {
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread t1 = new Thread(()->{
                System.out.println("aaaaaaaa1");
                try {
                    LockSupport.park();
                    System.out.println("aaaaaaa2");
                }catch (Exception e){
    
                }
            });
    
            Thread t2 = new Thread(()->{
                try {
                    Thread.sleep(1000);
                    System.out.println("bbbbbbbb1");
                    Thread.sleep(3000);
                    System.out.println("bbbbbbbbb2");
                    LockSupport.unpark(t1);
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            t1.start();
            t2.start();
        }
    }

    4.2 执行结果

      t1线程执行,调用park方法,等待被唤醒。t2线程同时执行,调用unpark唤醒t1线程,t1线程继续执行。

      它和前面相比,没有用到锁,阻塞更少。

    aaaaaaaa1
    bbbbbbbb1
    bbbbbbbbb2
    aaaaaaa2

     4.3 注意事项

      1)park和unpark的调用顺序随意

      2)调用park方法实际上是将该线程的permit设置为0,它会被阻塞,需要其它线程unpark它,就会把它的permit设置为1,它就会被唤醒

      3)permit的值只有0和1,所以,无论调用多少次unpark方法,permit的值都会是1,只能唤醒一次

      

    4.3.1 示例1

    public class LockTest5 {
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread t1 = new Thread(()->{
                System.out.println("aaaaaaaa1");
                try {
                    LockSupport.park();
                    System.out.println("aaaaaaaaa2");
                    LockSupport.park();
                    System.out.println("aaaaaaa3");
                }catch (Exception e){
    
                }
            });
    
            Thread t2 = new Thread(()->{
                try {
                    Thread.sleep(1000);
                    System.out.println("bbbbbbbb1");
                    Thread.sleep(3000);
                    System.out.println("bbbbbbbbb2");
                    LockSupport.unpark(t1);
                    LockSupport.unpark(t1);
                    System.out.println("bbbbbbbbb3");
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            t1.start();
            t2.start();
        }
    }

    执行结果

      t1调用了两次park方法,t2虽然为t1调用了两次unpark方法,但是aaaaa3没有打印出来,t1一直阻塞着。因为t1最多只能拿一张通行证,有两个路卡,就只能过一个

    aaaaaaaa1
    bbbbbbbb1
    bbbbbbbbb2
    bbbbbbbbb3
    aaaaaaaaa2

    4.3.2 示例2

    public class LockTest5 {
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread t1 = new Thread(()->{
                System.out.println("aaaaaaaa1");
                try {
                    LockSupport.park();
                    System.out.println("aaaaaaaaa2");
    
                    LockSupport.park();
                    System.out.println("aaaaaaa3");
                }catch (Exception e){
    
                }
            });
    
            Thread t2 = new Thread(()->{
                try {
                    Thread.sleep(1000);
                    System.out.println("bbbbbbbb1");
                    Thread.sleep(3000);
                    System.out.println("bbbbbbbbb2");
                    LockSupport.unpark(t1);
                    Thread.sleep(2000);
                    LockSupport.unpark(t1);
                    System.out.println("bbbbbbbbb3");
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            t1.start();
            t2.start();
        }
    }

    执行结果

       发现aaaaaa3打印出来了,因为两个unpark之间停顿了2秒,第一个unpark给t1发了一张通行证,过了第一个关卡,permit又变为0了,等了2秒,第二个unpark方法又给它发了一张通行证,过了第二个关卡。

    aaaaaaaa1
    bbbbbbbb1
    bbbbbbbbb2
    aaaaaaaaa2
    bbbbbbbbb3
    aaaaaaa3
    
    Process finished with exit code 0

    5 几种等待的特点及区别

      是否是否锁 是否传入等待时间 唤醒 方法调用前提 调用顺序 其它说明
    Thread.sleep 必须 时间到自动醒      
    object.wait+notify 可传可不传

    传时间:时间到自动醒

    不传时间:调用notify方法唤醒

    必须在synchronized中

    wait在前

    notify在后

     
    condition.await+signal 可传可不传

    传时间:时间到自动醒

    不传时间:调用signal方法唤醒

    必须在Lock unlock之间使用

    await在前

    signal在后

     
    LockSupport.park+unpark 不用传 调用unpark方法唤醒   park和unpark顺序随意

    1)调用park方法实际上是

    将该线程的permit设置为0,

    它会被阻塞,需要其它线程

    unpark它就会把它的permit设置为1,

    它就会被唤醒

    2)permit的值只有0和1,所以,

    无论调用多少次unpark方法,

    permit的值都会是1,只能唤醒一次

    6 示例证明LockSupport不会释放锁

    public class Test1 {
    
        //测试park方法是否会释放锁
    
        public static void main(String[] args) {
    
            Object o = new Object();
    
            Thread t1 = new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "执行");
                synchronized (o){
                    System.out.println(Thread.currentThread().getName() + "拿到锁");
                    LockSupport.park();
                    System.out.println(Thread.currentThread().getName() + "执行结束");
                }
            }, "线程1");
    
            Thread t2 = new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "执行");
                synchronized (o){
                    System.out.println(Thread.currentThread().getName() + "拿到锁");
                    LockSupport.unpark(t1);
                    System.out.println(Thread.currentThread().getName() + "执行结束");
                }
            }, "线程2");
    
            t1.start();
            try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); }
            t2.start();
        }
    }

    执行结果,一直阻塞着,没有执行完

    在线程1获取锁后,调用park方法进入等待状态,线程2没有拿到锁,一致处于阻塞状态,说明线程1进入等待时,没有释放锁

    线程1执行
    线程1拿到锁
    线程2执行

    7 示例(证明object.wait会释放锁)

    public class Test2 {
    
        //测试object.wait方法是否会释放锁
    
        public static void main(String[] args) {
    
            Object o = new Object();
    
            Thread t1 = new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "执行");
                synchronized (o){
                    System.out.println(Thread.currentThread().getName() + "拿到锁");
                    try { o.wait(); } catch (InterruptedException e) { e.printStackTrace(); }
                    System.out.println(Thread.currentThread().getName() + "执行结束");
                }
            }, "线程1");
    
            Thread t2 = new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "执行");
                synchronized (o){
                    System.out.println(Thread.currentThread().getName() + "拿到锁");
                    o.notify();
                    System.out.println(Thread.currentThread().getName() + "执行结束");
                }
            }, "线程2");
    
            t1.start();
            try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); }
            t2.start();
        }

    执行结果,在线程1获取锁后,调用wait方法进入等待状态,线程2拿到锁了,说明线程1进入等待时,释放锁了

    线程1执行
    线程1拿到锁
    线程2执行
    线程2拿到锁
    线程2执行结束
    线程1执行结束

    8 线程状态迁移图

  • 相关阅读:
    1012 The Best Rank (25 分)(排序)
    1011. World Cup Betting (20)(查找元素)
    1009 Product of Polynomials (25 分)(模拟)
    1008 Elevator (20 分)(数学问题)
    1006 Sign In and Sign Out (25 分)(查找元素)
    1005 Spell It Right (20 分)(字符串处理)
    Kafka Connect 出现ERROR Failed to flush WorkerSourceTask{id=local-file-source-0}, timed out while wait
    flume、kafka、avro组成的消息系统
    Java23种设计模式总结【转载】
    Java编程 思维导图
  • 原文地址:https://www.cnblogs.com/jthr/p/16067930.html
Copyright © 2020-2023  润新知