java:用CountDownLatch.await替代Object.wait实现线程阻塞/唤醒

来源:互联网 发布:大数据和云计算的关系 编辑:程序博客网 时间:2024/06/16 00:55

线程之间经常需要一定的同步,比如线程A需要线程B的部分运算结果,但又不必等到线程B把所有的结果都算出来,否则A就要待太长时间。
下面这个例子就是这个应用场景,主线程需要等待子线程从数据库中加载记录,但是子线程把所有的记录都加载完要花挺长时间。
而实际上,主线程最开始只需要一条记录就可以继续自己的后续动作了。怎么办呢?下面的代码利用传统的Object.wait()/nofity()方法来实现:

    public void openSource() {        // 创建一个初值为1的倒数计数器对象作为通知对象        Object notifier=new Object();        // 启动新线程用于长时间的加载任务        new Thread(new Runnable() {            @Override            public void run() {                boolean isNotified=false;                try {                    int index = 0;                                      while (true) {                        {                        // 加载数据。。。。                        }                        // 加载第一条记录后,唤醒等待线程                        if (1 == ++index) {                            synchronized (notifier){                                notifier.notify();                                isNotified=true;                            }                                                   }                    }                } finally {                    //循环结束,不论有没有加载到数据记录,都执行唤醒,                    //以确保notify无论如何都会被执行一次,否则等待线程会一直阻塞                     synchronized (notifier){                        if(!isNotified)                            notifier.notify();                    }                }            }        }).start();             synchronized (notifier){            try {                // 启动子线程后立即阻塞, 等待被子线程唤醒                notifier.wait();            } catch (InterruptedException e) {}        }        // 确保子线程加载了第一记录后,主线程继续自己的工作。。。。    }

话说,用Object.wait()/notify()倒是挺直观,但是,这synchronized同步块代码写起来实在有点啰嗦,我是个一行代码都不想多写的懒人。
就在想这代码能不能 看着更简单点?
于是想到了java.util.concurrent包下的CountDownLatch,
这是个好东西,顾名思义,它实现了一个多线程环境下倒数计数器锁,当计数器倒数到0时,唤醒阻塞的线程,允许多个线程同时对计数器减1,用在这里实在大材小用了。不过管它呢,方便就成啊。
于是前面的代码就被我用CountDownLatch改造成下面这样:

    public void openSource() {        // 创建一个初值为1的倒数计数器锁        CountDownLatch notifier=new CountDownLatch(1);        new Thread(new Runnable() {            @Override            public void run() {                try {                    int index = 0;                    while (true) {                        {                        // 加载数据。。。。                        }                                               if (1 == ++index) {                            // 计数减1,唤醒等待线程                            notifier.countDown();                        }                    }                } finally {                    // 线程结束之前再执行一次唤醒动作                    notifier.countDown();                }            }        }).start();        try {            // 启动子线程后立即阻塞, 等待被倒数计数器锁唤醒            notifier.await();        } catch (InterruptedException e) {}    }

相比前面的代码,finally{}代码块中没有再判断是否已经执行过唤醒动作,为什么呢,因为countDown当计数已经归0的时候什么也不做,所以就算多执行一次countDown也完全不影响程序的逻辑。
没有烦人的synchronized同步块代码块后,代码是不是看起来简洁一点点呢?
当然相比原始的wait/nofity,CountDownLatch的实现是经过高度封装的代码,最终它也是用wait/nofity来实现的,但逻辑更复杂,所以性能上有多少影响我并不清楚,因为我的应用环境是在程序初始化的时候,并不是高频调用,所以我并不太关注这个。

0 0
原创粉丝点击