多线程与并发(三):notify与wait原理

2021-09-07  本文已影响0人  lilykeke

wait notify 原理

Monitor.jpg

1. API介绍

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

public class WaitNotifyTest {

    static final Object obj = new Object();
    
    public static void main(String[] args) {

        new Thread(()->{
            synchronized (obj) {
                System.out.println("ENTER " + Thread.currentThread().getName());
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("thread is " + Thread.currentThread().getName());
        },"t1").start();

        new Thread(()->{
            synchronized (obj) {
                System.out.println("ENTER " + Thread.currentThread().getName());
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("thread is " + Thread.currentThread().getName());
        },"t2").start();

        new Thread(()->{

            synchronized (obj) {
                System.out.println("ENTER NOTIFY ");
                obj.notify();
            }
            System.out.println("thread is " + Thread.currentThread().getName());
        },"t3").start();

    }
}

2. wait 和notify 使用

2.1 sleep(long n)和wait(long n)的区别

例子一
public class SleepTest {
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout;

    public static void main(String[] args) {

        new Thread(()->{
            synchronized (room) {
                System.out.println(Thread.currentThread().getName()+"有没有烟?" + hasCigarette);

                if(!hasCigarette){
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName()+"再问一遍有没有烟?" + hasCigarette);
                if (hasCigarette){
                    System.out.println( Thread.currentThread().getName()+"可以开始干活了");
                }
            }

        },"小男").start();

        for (int i = 0; i <5 ; i++) {
            new Thread(()->{
                synchronized (room){
                    System.out.println(Thread.currentThread().getName() + "可以开始干活了");
                }
            },"其他人" + i).start();
        }


        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            //synchronized (room){

            hasCigarette = true;
            System.out.println(Thread.currentThread().getName() + "送烟");
            //}
        },"小女").start();

    }
}

例子二
public class WaitNotifyTest1 {
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout;

    public static void main(String[] args) {

        new Thread(()->{
            synchronized (room) {
                System.out.println(Thread.currentThread().getName()+"有没有烟?" + hasCigarette);

                if(!hasCigarette){
                    try {
                      room.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName()+"再问一遍有没有烟?" + hasCigarette);
                if (hasCigarette){
                    System.out.println( Thread.currentThread().getName()+"可以开始干活了");
                }
            }

        },"小男").start();

        for (int i = 0; i <5 ; i++) {
            new Thread(()->{
                synchronized (room){
                    System.out.println(Thread.currentThread().getName() + "可以开始干活了");
                }
            },"其他人" + i).start();
        }

        new Thread(()->{
            synchronized (room){

            hasCigarette = true;
            room.notify();
            System.out.println(Thread.currentThread().getName() + "送烟");
            }
        },"小女").start();
    }
}

例子三
public class WaitNotifyTest2 {

    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout;

    public static void main(String[] args) {

        new Thread(() -> {
            synchronized (room) {
                System.out.println(Thread.currentThread().getName() + "有没有烟?" + hasCigarette);

                if (!hasCigarette) {
                    try {
                        room.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + "再问一遍有没有烟?" + hasCigarette);
                if (hasCigarette) {
                    System.out.println(Thread.currentThread().getName() + "可以开始干活了");
                }
            }

        }, "小男").start();


        new Thread(() -> {
            synchronized (room) {
                System.out.println(Thread.currentThread().getName() + "外卖送到没?" + hasTakeout);
                if (!hasTakeout) {
                    try {
                        room.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + "再问一遍有没有送到?" + hasCigarette);
                if (hasTakeout) {
                    System.out.println(Thread.currentThread().getName() + "可以开始干活了");
                }
            }
        }, "其他人" ).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            synchronized (room) {

                //hasCigarette = true;
                hasTakeout = true;
                room.notify();
                //room.notifyAll(); //唤醒所有等待的线程
                System.out.println(Thread.currentThread().getName() + "送到了");
            }
        }, "外卖").start();
    }
}

异步模式之生产者与消费者

要点

Park & Unpark

1. 基本使用

LockSupport 类中的方法

//暂停当前线程
LockSupport.park();

//恢复某个线程的运行
LockSupport.unpark(暂停线程对象)

示例:

public class Test2 {

    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("start park");
            LockSupport.park();
            System.out.println("restart");
        },"t1");

        t1.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("start unpark");
        LockSupport.unpark(t1);

    }
}

测试结果:
start park
start unpark
restart


注意两点:

  1. park 之后线程状态是什么 WAITING
  2. unpark 既可以在线程之前调用也可以在线程之后调用,区别是什么?
    下面原理部分说明

特点:
与Object 的wait 和notify相比

2. park unpark 原理

每个线程都有自己的一个Parker 对象,由三部分组成_counter, _cond,_mutex 打个比喻

  1. 当前线程调用Unsafe.park()方法
  2. 检查_counter,这个值为0,这时,获得_mutex互斥锁
  3. 线程进入_cond 条件变量阻塞
  4. 设置_counter = 0;
unpark.jpg
  1. 调用Unsafe.unpark(Thread-0)方法,设置_counter为1
  2. 唤醒_cond条件变量中的Thread-0
  3. Thread-0 恢复运行
  4. 设置_counter =0

重新理解线程状态转换

线程状态转换.jpg

假设有线程Thread t

情况一: NEW --> RUNNABLE

当调用t.start()方法时,由NEW --> RUNNABLE

情况二:RUNNABLE <-->WAITING

t线程用synchronized(obj) 获取了对象锁之后

情况三 :RUNNABLE <--> WAITING

情况四:RUNNABLE <--> WAITING

情况五:RUNNABLE <-->TIMED_WAITING

t 线程调用synchronized(obj)获取了对象锁

情况六: RUNNABLE <--> TIMED_WAITING

情况七: RUNNABLE <--> TIMED_WAITING

情况八: RUNNABLE <--> TIMED_WAITING

情况九: RUNNABLE <--> BLOCKED

情况十: RUNNABLE <--> TERMINATED

当前线程所有代码运行完毕,进入TERMINATED

上一篇下一篇

猜你喜欢

热点阅读