Java并发编程之线程篇之线程间通信(四)(2)

电脑杂谈  发布时间:2019-08-27 18:03:07  来源:网络整理

在上文中,我们看到即使使用synchronized来实现泛型间的通信,我们应该结合Object中的配套方法wait()/notify()、wait()/notifyAll。我们先来说说一看Obejet中这系列方式的表明:

方法名称描述

wait()

调用该方式的线程处于WAITING状态,只有期待另外线程的通知或被中止才能返回,需要注意,线程读取wait()方法前,需要获取对象的监视器。当调用wait()方法后,会释放对象的监视器

wait(long)

调用该方式的轮询进入TIMED_WAITING状态,这里的参数时间是毫秒,等待对应毫秒事件,如果没有收到其他线程通知,则超时返回

wait(long,int)

调用该方式的轮询进入TIMED_WAITING状态,基本作用同wiat(long),第二个参数代表为纳秒,也就是等待时间为毫秒+纳秒。

notify()

通知一个在对象监视器上等待的轮询,使其从wait()方法返回,而返回的前提是该线程获得到了对象的监视器。

notifyAll()

通知所有在监视器上等待的线程,具体激活那个线程由CPU决定

使用Object的wait()/notify()、wait()/notifyall(),其实是我们常常使用的期待/通知体系,所谓的等待/通知体系是指一个线程A调用了对象O的wait()方法开启等待状态,而另一个线程B调用了对象O的notify或者notifyAll方法。线程A收到通知后从对象O的wait()方法返回,进而执行后续的操作。

下面,我们来借助一个例子来认识使用synchronized完成泛型的通信,具体实例如下所示:

class SynchronizedDemo {
    static boolean flag = true;
    static Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        new Thread(new WaitRunnable(), "WaitThread").start();
        TimeUnit.SECONDS.sleep(1);//这里睡眠,是保证Wait线程先执行
        new Thread(new NotifyRunnable(), "NotifyThread").start();
    }
    static class WaitRunnable implements Runnable {
        @Override
        public void run() {
            synchronized (lock) {
                while (flag) {//注意,通过while循环来判断条件
                    String name = Thread.currentThread().getName();
                    try {
                        System.out.println(name + "--->wait in " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(name + "--->wake up in " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                }
            }
        }
    }
    static class NotifyRunnable implements Runnable {
        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            synchronized (lock) {
                System.out.println(name + "--->notify all in " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                lock.notifyAll();
                flag = false;
            }
            /**
             * 这里再次加锁,是为了验证当调用对象的notifyAll方法时,
             * 如果线程不执行monitorexit(也就是释放锁),那么是不会唤醒其他线程的
             */
            synchronized (lock) {
                try {
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(name + "--->hold lock again in " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

输出结果如下:

linux 线程 死锁_线程死锁_多线程死锁的伪代码

WaitThread--->wait in 23:10:11
NotifyThread--->notify all in 23:10:12
NotifyThread--->hold lock again in 23:10:14
WaitThread--->wake up in 23:10:14

从上文中,我们可以得出下面结论:

除了使用synchronized完成泛型的通信之外,我们还可以使用courrent包下的Lock接口,这里以ReentrantLock为例。具体实例如下所示:

class LockDemo {
    static boolean flag = true;
    static Lock lock = new ReentrantLock();
    static Condition codition = lock.newCondition();
    public static void main(String[] args) throws InterruptedException {
        new Thread(new WaitRunnable(), "WaitThread").start();
        TimeUnit.SECONDS.sleep(1);//这里睡眠,是保证Wait线程先执行
        new Thread(new NotifyRunnable(), "NotifyThread").start();
    }
    static class WaitRunnable implements Runnable {
        @Override
        public void run() {
            lock.lock();
            try {
                while (flag) {
                    String name = Thread.currentThread().getName();
                    System.out.println(name + "--->wait in " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                    codition.await();
                    System.out.println(name + "--->wake up in " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    static class NotifyRunnable implements Runnable {
        @Override
        public void run() {
            lock.lock();
            try {
                String name = Thread.currentThread().getName();
                System.out.println(name + "--->notify all in " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                flag = false;
                codition.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }
}

输出结果:

WaitThread--->wait in 23:39:34
NotifyThread--->notify all in 23:39:35
WaitThread--->wake up in 23:39:35

关于Lock使用及机理,大家可以查看下面几篇文章,这里就不再进行分析了。

从上面的事例中,我们可以总结并受到特别经典的期待/通知形式,该模式分别对于期待方法(消费者)和通知方(生产者)。

等待方依照如下原则:

获取对象的锁。如果条件不满足,那么条件不满足,那么调用对象的wait()方法。被通知后一直要再次检查条件。条件满足则执行相应逻辑。

对应伪代码分别如下:

使用synchronized方法:

synchronized(对象) {
    while(条件不满足){
        对象.wait();
    }
    对应的处理逻辑
}

使用lock模式:

    lock.lock();
    try{
        while(条件不满足){
            condition.wait();
        }
    }finally{
        lock.unlock();
    }


本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-120751-2.html

相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    • 文珏
      文珏

      现在需要探讨的是对美斗争的策略

    • 田亚宾
      田亚宾

      一年估计只有加多宝赚的钱的零头的十分之一吧

      • 陈申公妫犀侯
        陈申公妫犀侯

        中国还没有宣布这三个新岛的领海基点吧

    • 隐之王
      隐之王

      懂吗

    热点图片
    拼命载入中...