BastenGao's Blog - Web, Rails, Ruby, Java

java中将异步转化为同步


java sync async 写于2014-03-12

从大家学习第一门程序语言起,代码基本都是顺序执行的。这点毋庸置疑,把大象关进冰箱也是顺序执行。 但是计算机的硬件性能在提升,相应地软件能够为了充分利用cpu资源,也将是多线程的。如果程序还是单线程,顺序执行恐怕你现在还看不到我的这篇博客。

一旦说道多线程就会有异步,同步,线程安全这些东西。关于线程安全,推荐Java并发编程实战这本书,这次主要说如何将异步转化为同步.

写过 javascript 人都知道 javascript 中是没有 sleep 方法的,就是 javascript 里所有的处理都是异步的,sleep 要通过 setTimeout 来实现。

// sleep 5000 millis
setTimeout(function(){
  console.log("i will back after 5000 millis.")
}, 5000);

上面代码中的匿名函数不是马上执行的,而是 5 秒后,系统调用。包括大家平时用的 ajax , 同样也是异步操作。

不像 javascript,java 则需要 Thread 来并发执行。通常 web 服务器或者 tcp/udp 服务,如果用同步的 i/o api,都需要使用多线程提高系统的吞吐。 有时候为了效率会将同步的业务拆成异步的,例如用户注册,同时发送验证邮件,但发送邮件可能会比较耗时,这样用户会有长时间的等待,为了提升用户体验,我们可以将发送邮件放在后台线程中去执行。但是多线程会打乱我们平时认为代码顺序执行的习惯,所以写出来的代码会更难理解和测试。

同样我们有时候却需要将异步转化为同步,或者看起来是同步。例如需要等待另一个异步操作的结果。

现在我们步入正题,可以通过以下方式将异步转化为同步。

synchronized keyword

final Object lock = new Object();

Thread asyncTask = new Thread(){

    @Override
    public void run(){ 
        // do something 
        synchronized(lock) {
            lock.notifyAll();
        }
    }
};
asyncTask.start();

synchronized(lock) {
    lock.wait();
}

Lock

Lock 相比 synchronized 关键字性能会略胜一筹。

final Lock lock = new ReentrenLog();
final Condition condition = lock.newCondition();

Thread asyncTask = new Thread(){

    @Override
    public void run(){ 
        // do something 
        lock.lock();
        try {
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
};

lock.lock();
try {
    condition.await();
} finally {
    lock.unlock();
}

lock util

除了 Lock, java 还提供了一些锁相关的工具类,例如 CountDownLatch 方便我们使用。

final CountDownLatch latch = new CountDownLatch(2);

Thread asyncTask = new Thread(){

    @Override
    public void run(){ 
        // do something 
        latch.countDown();
    }
};
asyncTask.start();

latch.countDown();

jdeferred

jdeferred 是一个类似于 jQuery.Deferred(可参考 阮一峰的博客) 的Java实现。 我简单地看了他的源代码,是基于 synchronzed 来实现的,用他主要还是因为他的接口简单(与 jQuery.Deferred 相似),无他。

final Deferred deferred = new DeferredObject();

Thread asyncTask = new Thread(){

    @Override
    public void run(){ 
        // do something 
        deferred.resolve(null);
    }
};

Promise promise = deferred.promise();
promise.waitSafely();
comments powered by Disqus
Fork me on GitHub