Java通过Lock类提供了高级的锁特性,它在获取锁的时候可以做更灵活的控制,比如设置获取锁的最长等待时间。Lock可以与多个Condition对象配合,Lock替代synchronized进行共享对象的锁控制,而Condition替代了Object监控方法的使用,Object中的wait/notify/notifyAll方法,可对应Condition中的await/signal/sinallAll方法。Condition对象一般通过lock.newCondition()方法实例化,所以支持一个lock绑定多个Condition对象进行使用,可实现一个共享对象有多个阻塞集合。使用时需要注意以下规则:
1、object.wait方法调用前必须获取object对象锁,调用wait方法将释放该锁并阻塞线程,直到object.notify/notifyAll方法被调用时才有机会重新获取锁和继续执行;同样的,condition.await方法调用前必须获取关联lock的锁,调用await方法将释放该锁并阻塞线程,直到condition.signal/signalAll方法被调用时才有机会重新获取锁和继续执行;
2、object.notify/notifyAll方法被调用前必须获取到object对象锁,否则会抛异常;同样的,condition.signal/signalAll方法被调用前必须获取关联lock的锁。
Future接口通常用于异步计算场景,比如这样一种场景:底层采用jgroups等异步通信的方式进行远程交互,上层应用依赖底层异步通信的方式给用户提供各种功能。底层是异步通信,而大部分用户功能应该是同步的,如用户点了一个按钮或链接,他期望看到的显示是他上一次操作的结果。该如何搭建基于异步通信的同步机制呢?下面给出一种简单的方案:
1、实现异步消息发送、异步消息接收。
2、异步消息发送后,将发送的消息封装为Future后加入队列待匹配,阻塞线程,直到收到对应的返回结果,或者超时。
3、收到异步消息时,在队列中搜索匹配结果,如果匹配ok,将响应内容放到Futrue对象中,同时自动唤醒步骤2中的阻塞点。
以下是参考的Future实现类,实际业务逻辑中,消息发送后,new MsgFutrue(T)构造消息封装对象,加入队列,同时调用future的get方法阻塞线程。在消息接收线程中匹配异步消息对应关系(比如根据sessionID对应),执行步骤3。
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MsgFuture<T> implements Future<T> {
protected T request;
private T response;
private ReentrantLock lock;
private Condition wait;
private volatile boolean done;
public MsgFuture(T request) {
this.request = request;
lock = new ReentrantLock();
wait = lock.newCondition();
}
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
if (isDone()) {
return this.response;
}
this.lock.lock();
try {
this.wait.await(timeout, unit);
} catch (Exception e) {
// e.printStackTrace();
}
this.lock.unlock();
return this.response;
}
@Override
public T get() throws InterruptedException, ExecutionException {
this.lock.lock();
try {
this.wait.await();
} catch (Exception e) {
// e.printStackTrace();
}
this.lock.unlock();
return this.response;
}
public void done(T respone) {
this.lock.lock();
this.response = respone;
this.done = true;
this.wait.signal();
this.lock.unlock();
}
@Override
public boolean isDone() {
return done;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
}