线程的等待和唤醒机制

目录

    • 第一种方式:synchronized + wait + notify:
    • 第二种方式:Lock + await + signal :
    • 第三种方式:LockSupport + park +unpark :
    • LockSupport面试题:

线程的等待和唤醒机制_第1张图片

三种让线程等待和唤醒的方法:
在这里插入图片描述
LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程:

第一种方式:synchronized + wait + notify:

使用Object中的wait方法让线程等待,使用Object中的notify方法唤醒线程

线程的等待和唤醒机制_第2张图片

代码演示:

package com.fan.waitnotify;

public class SyncDemo {
    //定义锁
    static Object obj = new Object();
    public static void main(String[] args) {
        new Thread(()->{
            synchronized (obj){//加锁
                System.out.println(Thread.currentThread().getName()+
                        "\t ----come  in---");
                try {
                    obj.wait();//同一个锁去等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+
                        "被唤醒--");
            }
        },"A线程").start();

        //
        new Thread(()->{
            synchronized (obj){
                obj.notify();//同一个锁去唤醒
                System.out.println(Thread.currentThread().getName()+
                        "\t ---通知其他线程---");
            }
        },"B线程").start();
    }
}

存在的弊端:

问题一:wait和notify 不能脱离synchronized代码块或者synchronized方法独自运行
线程的等待和唤醒机制_第3张图片

线程的等待和唤醒机制_第4张图片
报异常,证明 wait + notify不能脱离synchronized 代码块或者方法

问题二:wait + notify顺序不能颠倒,只能先等待后唤醒
线程的等待和唤醒机制_第5张图片

打印结果:发现处于阻塞状态:
线程的等待和唤醒机制_第6张图片
被阻塞的结果,证明wait + notify顺序不能颠倒,只能先等待后唤醒。等待中的线程才能被唤醒,否则无法被唤醒

在这里插入图片描述

第二种方式:Lock + await + signal :

使用JUC包中的Condition的await方法让线程等待,使用signal方法唤醒线程

线程的等待和唤醒机制_第7张图片

代码演示:

package com.fan.waitnotify;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockDemo {
    //创建可重入的非公平锁
    static Lock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();
    public static void main(String[] args) {
        //如果这个方法我们只想运行一次,则直接写到线程内
        new Thread(()->{
            lock.lock();//加锁
            try {
                System.out.println(Thread.currentThread().getName()+
                        "\t -----进来了----");
                try {
                    condition.await();//等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //
                System.out.println(Thread.currentThread().getName()+
                        "\t -----被唤醒----");
            } finally {
                lock.unlock();//解锁
            }
        },"A").start();

        new Thread(()->{
            lock.lock();
            try {
                condition.signal();//唤醒
                System.out.println(Thread.currentThread().getName()+
                        "\t -----通知其他线程----");
            } finally {
                lock.unlock();
            }
        },"B").start();
    }
}

线程的等待和唤醒机制_第8张图片

存在的问题:

问题一:lock和await和signal必须一起使用才行,await和signal不能脱离lock单独使用;
线程的等待和唤醒机制_第9张图片
出现异常:证明lock和await和signal必须一起使用才行;
线程的等待和唤醒机制_第10张图片

问题二:必须要先等待,后唤醒;调换顺序不能正常运行;
线程的等待和唤醒机制_第11张图片

同样存在问题:必须要先等待,后唤醒;

线程的等待和唤醒机制_第12张图片

总结:传统的synchronized 和Lock实现等待唤醒通知的约束

在这里插入图片描述

第三种方式:LockSupport + park +unpark :

即LockSupport类的park方法可以 阻塞当前线程,使用unpark 方法唤醒指定被阻塞的线程

注意:
park相当于wait,unpark相当于notify

LockSupport是什么:

在这里插入图片描述

线程的等待和唤醒机制_第13张图片

线程的等待和唤醒机制_第14张图片

park方法:
线程的等待和唤醒机制_第15张图片

unpark方法:
线程的等待和唤醒机制_第16张图片

代码演示:

package com.fan.waitnotify;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
public class ParkDemo {
    public static void main(String[] args) {
        Thread A = new Thread(()->{
            //测试  先通知后阻塞:
            /*try { TimeUnit.SECONDS.sleep(3);}
            catch (InterruptedException e) {e.printStackTrace();}*/
            System.out.println(Thread.currentThread().
                    currentThread().getName()+"\t ----进来了----");
            LockSupport.park();//当前线程被阻塞
            System.out.println(Thread.currentThread().
                    currentThread().getName()+"\t ----被唤醒----");
        },"A");
        A.start();
        //让A线程先运行
        try { TimeUnit.SECONDS.sleep(1);}
        catch (InterruptedException e) {e.printStackTrace();}
        Thread B = new Thread(() -> {
            LockSupport.unpark(A);//唤醒 被阻塞的 线程实例
            System.out.println(Thread.currentThread().
                    currentThread().getName()+"\t ----通知了----");
        }, "B");
        B.start();
    }
}

线程的等待和唤醒机制_第17张图片
线程的等待和唤醒机制_第18张图片

测试 先通知后阻塞:发现可以正常运行;
线程的等待和唤醒机制_第19张图片
线程的等待和唤醒机制_第20张图片
在这里插入图片描述
线程的等待和唤醒机制_第21张图片

重点:
线程的等待和唤醒机制_第22张图片

LockSupport面试题:

线程的等待和唤醒机制_第23张图片
线程的等待和唤醒机制_第24张图片

线程的等待和唤醒机制_第25张图片

你可能感兴趣的:(多线程,面试,java,程序人生,开发语言)