网络程序(或在其他异步的程序)由于是异步返回结果的,通常需要阻塞线程直到结果返回,这个时候,下面的代码就有用了。
package org.jilen.cookie.concurrent;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* This is a simple <strong>future</strong> implementation which <strong>allow
* client to set value manually</strong>. <br/>
* All get request are blocked before value is set. <br/>
* Yes, the <code> new ArrayBlockingQueue(1);</code> can perform the same task.
* But this version is much more lightweight
*
* @author jilen.zhang@gmail.com
* @param <V>
*/
public class WaitForValueFuture<V> {
/**
* latch used to block before value is set
*/
private CountDownLatch latch = new CountDownLatch(1);
private volatile boolean isValueSet = false;
private AtomicBoolean isDone = new AtomicBoolean(false) ;
/**
* @return whether the value is set already
*/
public boolean isDone() {
return isValueSet;
}
/**
* @return get the value, if value is not set, block until it is set
* @throws InterruptedException
*/
public V get() throws InterruptedException {
latch.await();
return value;
}
/**
* get the value within specified time
*
* @param timeout
* @param unit
* @return
* @throws InterruptedException
* @throws TimeoutException
*/
public V get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {
latch.await(timeout, unit);
return value;
}
/**
* binding value to this future
*
* @param toBeSet
*/
public void set(V toBeSet) {
if(isDone.compareAndSet(false, true)){
this.value = toBeSet;
latch.countDown();
} else {
throw new IllegalStateException("value is set already");
}
}
}
下面是一个silly的使用程序
package org.jilen.cookie.concurrent;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
public class WaitForValueFutureTest {
public static void main(String... args) {
Client sample = new Client();
Object message = sample.getValueFromSomewhere();
System.out.println(message);
}
}
class Client {
private static Map<Object, WaitForValueFuture<Object>> resultMap = new ConcurrentHashMap<Object, WaitForValueFuture<Object>>();
private static AtomicInteger idGenerator = new AtomicInteger(0);
public Object getValueFromSomewhere() {
//generate a request id, thus after receive response, the value can be set to the future in the result with this key
Integer requestId = idGenerator.getAndDecrement();
WaitForValueFuture<Object> future = new WaitForValueFuture<Object>();
resultMap.put(requestId, future);
sendGetRequest(requestId, "hello");
try {
return future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//prevent memory leak
resultMap.remove(requestId);
}
return null;
}
private void sendGetRequest(Integer requestId, Object message) {
System.out.println("peformming get request");
//may be sent request to remote server,just sleep instead
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//within an nio application, this method may be invoke while read event happend
receiveResponse(requestId, "hello world");
}
private void receiveResponse(Integer requestId, Object message) {
//fill the result
resultMap.get(requestId).set(message);
}
}