同步获取异步任务结果
思路:首先从jdk提供的框架和代码入手,然后再自己定义一个,
原创qq作者:855189478
1、CountDownLatch
使用同步框架可以实现同步,但是不能获取到异步的结果
参考代码如下:
import java.util.concurrent.CountDownLatch;
public class FutureTest {
public static void main(String[] args) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(1);
new Thread(()->{
try {
//线程等待模拟异步耗时任务,比如io(socket有关)
Thread.sleep(2000);
//当信号量减少为0,则await被唤醒且信号量
countDownLatch.countDown();
countDownLatch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
//阻塞了,同步等待异步执行完成。
countDownLatch.await();
System.out.println("异步任务执行完成");
}
}
/**
关于CountDownLatch使用
1、构造函数传入初始信号量
2、通过countDown来降低1个信号量,当信号量为0时await就会被唤醒。
3、使用场景:一个阶段多任务完成后再进入下一个阶段,即阶段划分。
比如多个线程执行完才执行某段代码
4、 若在调用await()时 信号量不为0,则await阻塞
若调用await()是,信号量已经为0,则不阻塞
当信号量为0时,调用countDown无任何影响,因为信号量最少是0。
*/
2、FutureTask
将异步任务封装到FutureTask中交由线程池执行
学习FutureTask(更多参考Future实现类)
实例一:
//错误用法
FutureTask<String> task=new FutureTask(()->{
System.out.println(Thread.currentThread().getName());
Thread.sleep(2000);
return "结果";
});
task.run();
//如果不调用task.run(); 则task.get()会一直阻塞
//因为任务未执行所以内部状态未变化,run后状态变化不阻塞,但是都是main线程执行的。
System.out.println(task.get());
实例二、
//run方法通常线程池会自动调用,就提交线程池
ExecutorService executorService = Executors.newFixedThreadPool(1);
FutureTask<String> task=new FutureTask(()->{
System.out.println(Thread.currentThread().getName());
Thread.sleep(2000);
return "结果";
});
//FutureTask实现了Runnable接口,线程池自动调用run方法,
//从而状态改变后task.get()解除阻塞,同时获取到结果
executorService.submit(callable);
System.out.println(task.get());
//线程池可以提交Runnable和Callable 子类
Callable实例补充:
ExecutorService executorService = Executors.newFixedThreadPool(1);
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName());
Thread.sleep(2000);
return "结果";
}
};
Future f= executorService.submit(callable);
System.out.println(f.get());
3、自己实现
Future方式是将耗时任务提交到线程池处理,和我们实现获取异步任务结果还是有区别的:
1、当我们需要同步获取回调函数中的结果使用Future就不合适了。
2、当我们提交同步任务到线程池,Future比较合适。
3、接下来实现:同步获取回调函数中的返回结果。
需求来源:
//实例说明,如下是zk连接,异步回调,getNewClient执行完需要通过回调才知道连接建立是否成功还是异常。
//思考:如何保证getNewClient执行结束可以获取到连接结果?
/**
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
<version>4.0.0</version>
</dependency>
*/
private CuratorFramework getNewClient() {
//间隔5秒重试3次
RetryPolicy retryPolicy = new ExponentialBackoffRetry(5000, 3);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(this.connectionUrl)
.retryPolicy(retryPolicy)
.namespace("chat")
.sessionTimeoutMs(3000)
.build();
client.getUnhandledErrorListenable().addListener((message, ex) -> {
System.err.println("error=" + message);
ex.printStackTrace();
promsie.tryFailure(ex);
});
client.getConnectionStateListenable().addListener((c, newState) -> {
System.out.println("state=" + newState);
if (newState== ConnectionState.CONNECTED){
promsie.trySuccess("zk获取连接成功");
}
});
client.start();
//
}
同步获取回调结果:
实现逻辑:方法返回前先阻塞,等回调执行完成再返回。
如果 client.start();能返回Future,我们只需要Future.get()就可以,但是实际没有,
可能因为回调函数多个,我们需求不一样,需要根据自己需求定制。
实现参考:使用过netty的应该知道,netty中定义了ChannelPromise接口来实现异步返回结果和异常。
第一版实现代码:
//说明:如果阅读有困难,将SyncProimseListener部分忽略
package promise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class SyncPromise<V> {
private static final Logger logger = LoggerFactory.getLogger(SyncPromise.class);
private Object lock = new Object();
//用于不同线程之间传递数据,即tryXX到get的传递
private volatile Object result;
private static final Object SUCCESS = new Object();
//用于无锁化处理
private static final AtomicReferenceFieldUpdater<SyncPromise, Object> RESULT_UPDATER =
AtomicReferenceFieldUpdater.newUpdater(SyncPromise.class, Object.class, "result");
private final ArrayList<SyncProimseListener> notifyListeners = new ArrayList<>();
/**
* 设置状态,并通知wait线程(get引起),无异常
*
* @param result 可以设置一个状态数据
*/
public boolean trySuccess(V result) {
return setValue0(result==null?SUCCESS:result);
}
/**
*
* @param cause 对应get时返回的异常
* @return true:set和listener首次执行完成,false:失败未执行
*/
public boolean tryFailure(Throwable cause) {
//代码优化:采用无锁处理 result的赋值
if (cause==null){
throw new NullPointerException("cause");
}
return setValue0(cause);
}
private boolean setValue0(Object objResult){
if (RESULT_UPDATER.compareAndSet(this, null, objResult)){
//确保只有一次机会来唤醒get阻塞,失败唤醒。
if (checkNotifyWaiters()) {
notifyListeners();
}
return true;
}
return false;
}
public boolean isDone() {
return isDone0(result);
}
private boolean isDone0(Object result) {
return result != null;
}
private short waiters;
private void incWaiters() {
if (waiters == Short.MAX_VALUE) {
throw new IllegalStateException("too many waiters: " + this);
}
++waiters;
}
private void decWaiters() {
--waiters;
}
/**
* 唤醒等待的线程(get引起的等待)
* @return {@code true} if there are listeners
*/
private synchronized boolean checkNotifyWaiters() {
if (waiters > 0) {
notifyAll();
}
return this.notifyListeners.size() > 0;
}
private void notifyListeners() {
synchronized (this) {
if (this.notifyListeners == null) {
return;
}
for (SyncProimseListener l : this.notifyListeners) {
try {
l.operationComplete(this);
} catch (Exception e) {
if (logger.isWarnEnabled()) {
logger.warn("An exception was thrown by " +
l.getClass().getName() + ".operationComplete()", e);
}
}
}
}
}
public V get() throws InterruptedException, ExecutionException {
Object result = this.result;
//等待完成的处理
if (!isDone0(result)) {
await();
result = this.result;
}
//正确完成的处理 success
if (result == SUCCESS) {
return null;
}
//错误完成的处理,cause
Throwable cause = cause0(result);
if (cause == null) {
return (V) result;
}
//返回tryFailure设置的异常
throw new ExecutionException(cause);
}
public Throwable cause() {
return cause0(result);
}
private Throwable cause0(Object result) {
if (!(result instanceof Throwable)) {
return null;
}
return (Throwable) result;
}
private SyncPromise<V> await() throws InterruptedException {
if (isDone()) {
return this;
}
if (Thread.interrupted()) {
throw new InterruptedException(toString());
}
synchronized (this) {
while (!isDone()) {
incWaiters();
try {
wait();
} finally {
decWaiters();
}
}
}
return this;
}
public void addListener(SyncProimseListener<SyncPromise> listener) {
if (listener==null){
throw new NullPointerException("listener");
}
synchronized (this) {
this.notifyListeners.add(listener);
}
}
public void removeListener(SyncProimseListener<SyncPromise> listener) {
synchronized (lock) {
for (int i = 0; i < this.notifyListeners.size(); i++) {
if (listener == this.notifyListeners.get(i)) {
this.notifyListeners.remove(listener);
break;
}
}
}
}
}
/**
补充说明:
1、代码看起来长,实际原理是:
get()方法会阻塞线程可以多次调用,trySuccess或tryFailure来唤醒阻塞的多个线程。
get()返回结果或者抛出异常,从而实现可以获取异步回调中的结果,实现方法同步。
*/
第一版调用实例:
import java.util.concurrent.ExecutionException;
public class TestPromise {
public static void main(String[] args) {
//通过SyncPromise同步实现获取回到结果。
SyncPromise<String> p = new SyncPromise();
//用单独线程模拟回调
new Thread(()->{
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//执行后会唤醒由于get()导致的线程等待,也就是就是Object.notifyAll( )
p.trySuccess("成功");
//这个导致get()抛出异常方式
//p.tryFailure(new Exception("test"));
}).start();
try {
//阻塞等待被唤醒,并返回结果
String s= p.get();
} catch (InterruptedException e) {
//有阻塞就可能有中断异常
e.printStackTrace();
} catch (ExecutionException e) {
//自己处理
e.printStackTrace();
}
}
}
第二版:代码优化
SyncPromise中方法太多,私有共有混到一块,我们用内部类来调整下。
public class SyncPromise<V> {
private static final Logger logger = LoggerFactory.getLogger(SyncPromise.class);
//用户未传入时的替代参数
private static final Object SUCCESS = new Object();
//用于不同线程之间传递数据,即tryXX到get的传递
private volatile Object result;
//用于无锁化处理
private static final AtomicReferenceFieldUpdater<SyncPromise, Object> RESULT_UPDATER =
AtomicReferenceFieldUpdater.newUpdater(SyncPromise.class, Object.class, "result");
private Inner<V> inner;
public SyncPromise() {
inner = new Inner<>();
}
/**
* 设置状态,并通知wait线程(get引起),无异常
*
* @param result 可以设置一个状态数据
*/
public boolean trySuccess(V result) {
return inner.setValue0(result == null ? SUCCESS : result);
}
/**
* @param cause 对应get时返回的异常
* @return true:set和listener首次执行完成,false:失败未执行
*/
public boolean tryFailure(Throwable cause) {
//代码优化:采用无锁处理 result的赋值
if (cause == null) {
throw new NullPointerException("cause");
}
return inner.setValue0(cause);
}
public V get() throws InterruptedException, ExecutionException {
Object result = this.result;
//等待完成的处理
if (!inner.isDone0(result)) {
inner.await();
result = this.result;
}
//正确完成的处理 success
if (result == SUCCESS) {
return null;
}
//错误完成的处理,cause
Throwable cause = inner.cause0(result);
if (cause == null) {
return (V) result;
}
//返回tryFailure设置的异常
throw new ExecutionException(cause);
}
//以下是内部类,方法和属性都不公开 ,调用方法参考上边即可
final class Inner<V> {
private Object listenerLock = new Object();
//复合操作还是用自定义锁
//private final List<SyncProimseListener> notifyListeners = Collections.synchronizedList(new ArrayList<SyncProimseListener>());
private final List<SyncProimseListener> notifyListeners = new ArrayList<SyncProimseListener>();
private boolean setValue0(Object objResult) {
if (RESULT_UPDATER.compareAndSet(SyncPromise.this, null, objResult)) {
//确保只有一次机会来唤醒get阻塞,失败唤醒。
if (checkNotifyWaiters()) {
notifyListeners();
}
return true;
}
return false;
}
public boolean isDone() {
return isDone0(result);
}
private boolean isDone0(Object result) {
return result != null;
}
private short waiters;
private void incWaiters() {
if (waiters == Short.MAX_VALUE) {
throw new IllegalStateException("too many waiters: " + this);
}
++waiters;
}
private void decWaiters() {
--waiters;
}
/**
* 唤醒等待的线程(get引起的等待)
*
* @return {@code true} if there are listeners
*/
private synchronized boolean checkNotifyWaiters() {
if (waiters > 0) {
notifyAll();
}
return this.notifyListeners.size() > 0;
}
private void notifyListeners() {
synchronized (this) {
if (this.notifyListeners == null) {
return;
}
for (SyncProimseListener l : this.notifyListeners) {
try {
l.operationComplete(this);
} catch (Exception e) {
if (logger.isWarnEnabled()) {
logger.warn("An exception was thrown by " + l.getClass().getName() + ".operationComplete()", e);
}
}
}
}
}
public Throwable cause() {
return cause0(result);
}
private Throwable cause0(Object result) {
if (!(result instanceof Throwable)) {
return null;
}
return (Throwable) result;
}
private void await() throws InterruptedException {
if (isDone()) {
return;
}
if (Thread.interrupted()) {
throw new InterruptedException(toString());
}
synchronized (this) {
while (!isDone()) {
incWaiters();
try {
System.out.println(Thread.currentThread().getName());
wait();
} finally {
decWaiters();
}
}
}
}
public void addListener(SyncProimseListener<SyncPromise> listener) {
if (listener == null) {
throw new NullPointerException("listener");
}
synchronized (listenerLock) {
this.notifyListeners.add(listener);
}
}
public void removeListener(SyncProimseListener<SyncPromise> listener) {
synchronized (listenerLock) {
for (int i = 0; i < this.notifyListeners.size(); i++) {
if (listener == this.notifyListeners.get(i)) {
this.notifyListeners.remove(listener);
break;
}
}
}
}
}
}
SyncProimseListener
public interface SyncProimseListener<SyncPromise> {
void operationComplete(SyncPromise syncPromise) throws Exception;
}
第二版调用:
调用方式和第一版完全一样。
关于第二版:
1、trySuccess和tryFailure 只能执行一次,因为变量只能设置一次。
2、get()可以在多个线程调用,由tryxx来唤醒。
3、listener使用场景:
listener调用关键代码:
private boolean setValue0(Object objResult) {
if (RESULT_UPDATER.compareAndSet(SyncPromise.this, null, objResult)) {
//确保只有一次机会来唤醒get阻塞,失败唤醒。
if (checkNotifyWaiters()) {
notifyListeners();
}
return true;
}
return false;
}
//可以自定义来实现具体需要
1)比如首次try不调用listener,后续try调用触发listener
如下即可
private boolean setValue0(Object objResult) {
if (RESULT_UPDATER.compareAndSet(SyncPromise.this, null, objResult)) {
return true;
}
if (checkNotifyWaiters()) {
notifyListeners();
}
return false;
}
场景:zk连接不稳定回调多次调用场景。
或者不用这个listener,自己定义回调替代SyncPromise中listener也可以的。
第一版和第二版:
代码实现借鉴 io.netty.channel.DefaultChannelPromise 。