JAVA 多线程(6):等待、通知 (1)

一、wait、notify、notifAll

所有Object都有这三个方法。

wait :当前线程等待锁(放弃当前线程持有的锁)

notify:随机通知等待此锁的线程准备获取锁

notifyALL:释放锁并通知所有等待此锁的线程

整个等待与通知的过程,类似餐馆上菜,服务员等待菜,厨师(线程)做好菜(锁)通知服务员(线程)。

在等待通知的过程中,必须要有锁的存在,也就是说必须要持有锁,才能进行等待与通知,如果没有则会出现异常。

如下:

public static void test(TestWait lock){
        synchronized (lock){
            System.out.println(Thread.currentThread().getName()+":"+"等菜");
            try {
                lock.wait();
                System.out.println(Thread.currentThread().getName()+":"+"上完了,再点菜");
                lock.notify();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void test2(TestWait lock){
        synchronized (lock){
            System.out.println(Thread.currentThread().getName()+":"+"做好了");
            try {
                lock.notify();
                System.out.println(Thread.currentThread().getName()+":"+"等客人");
                lock.wait();
                System.out.println("厨师发现没有原料了,下班");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestWait testWait = new TestWait();

        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                    test(testWait);
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                test2(testWait);
            }
        });
        t.start();
        Thread.sleep(1000);
        t2.start();
    }

输出:

由结果看出,线程1与线程2通过wait、notify 实现了通信(前提是锁一致)

notify针对的是随机通知一个线程,如果争夺锁的线程太多,而又一个线程的优先级打个比方较低,有可能会一致都争夺不到锁。

上面的这种可能性较低,但是如果调用notify次数不够怎么办,假设有A,B 2个线程调用wait方法放弃锁,然后等待,线程C 拿到锁后调用notify,那么线程A与线程B只有一个能获取到锁,另一个只能继续等待通知,等待一个永远获取不到的锁。

这时候就必须通过notifyAll 来通知A和B,这样把wait状态的他们解放出来,加入到争夺锁的队列中去。

还有一种情况就是通知过早,如果线程Await,线程Bnotify,锁为C,如果线程B先通知了,那么A 就永远在等待C锁了。

如果遇到感觉可能会出现这种情况,最好加上一个标识用来判断是否有必要等待。

private static boolean isWait = true;

    public static void test(TestWait lock){
        synchronized (lock){
            try {
                // 还要不要等菜
                if(isWait){
                    lock.wait();
                    System.out.println(Thread.currentThread().getName()+":"+"等菜");
                }
                System.out.println(Thread.currentThread().getName()+":"+"上完了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void test2(TestWait lock){
        synchronized (lock){
            System.out.println(Thread.currentThread().getName()+":"+"做好了");
            lock.notify();
            isWait = false;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestWait testWait = new TestWait();

        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                    test(testWait);
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                test2(testWait);
            }
        });

        Thread A = new Thread(t);
        t2.start();
        Thread.sleep(2000);
        A.start();
    }

输出:

结果看出,服务员不要再等待了,菜已经做好,直接上就行了。

wait等待造成状态改变

多个现在执行wait等待后,在某些条件改变后,有可能会出现意想不到的异常,如:

private static boolean isWait = true;
    private static List list = new ArrayList();

    public static void test(TestWait lock){
        synchronized (lock){
            try {
                // 还要不要等菜
                if(isWait){
            if(list.size() == 0){   lock.wait();   System.out.println(Thread.currentThread().getName()
+":"+"等菜");
            } } list.remove(
0); System.out.println(Thread.currentThread().getName()+":"+"上完了"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void test2(TestWait lock){ synchronized (lock){ System.out.println(Thread.currentThread().getName()+":"+"做好了"); list.add("菜"); lock.notifyAll(); isWait = false; } } public static void main(String[] args) throws InterruptedException { TestWait testWait = new TestWait(); Thread t = new Thread(new Runnable() { @Override public void run() { test(testWait); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { test2(testWait); } }); Thread A = new Thread(t); A.start(); Thread B = new Thread(t); B.start(); Thread.sleep(2000); t2.start(); }

输出:

 

结果是越界了,因为厨师只做了一道菜。

解决方法:轮询

while (list.size() == 0){
                        System.out.println(Thread.currentThread().getName()+":"+"等菜");
                        lock.wait();
                    }

输出:

第二个服务员继续等菜。(此事程序未结束)

生存者/消费者

private String str = "";
    class P {
        public void setV(Object o){
            synchronized (o){
                if(!str.equals("")){
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                str = Math.random() + "";
                System.out.println("Set的值:"+str);
                o.notifyAll();
            }

        }
    }

    class C {
        public void getV(Object o){
            synchronized (o){
                if(str.equals("")){
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Get的值:"+str);
                str = "";
                o.notifyAll();
            }
        }
    }

    public static void main(String[] args){
        Object lock = new Object();
        TestPC pc = new TestPC();
        P p = pc.new P();
        C c = pc.new C();
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    c.getV(lock);
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    p.setV(lock);
                }
            }
        });

        t.start();
        t2.start();
    }

 输出:

 上面是1对1,所以使用notify也是可以的,但是如果为多生产和多消费的话,就必须使用notifyAll 了。

还有如果对应的是1生产多消费,那么除了使用notifyAll 还要使用轮询机制,也就是while,类似上面的2个服务员一个厨师的例子。

成灰之前,抓紧时间做点事!!
原文地址:https://www.cnblogs.com/jony-it/p/10816809.html