Java并发(二)异步转同步
目录
前置条件:构造一个异步调用
一、使用wait和notify方法
二、使用条件锁
三、Future
四、使用CountDownLatch
五、使用CyclicBarrier
总结
在Java并发编程中,经常会因为需要提高响应速度而将请求异步化,即将同步请求转化为异步处理,这是很自然能想到的一种处理方式。相反,在有些场景下也需要将异步处理转化为同步的方式。
首先介绍一下同步调用和异步调用的概念:
同步调用:调用方在调用过程中,持续等待返回结果。
异步调用:调用方在调用过程中,不直接等待返回结果,而是执行其他任务,结果返回形式通常为回调函数。
其实,两者的区别还是很明显的,这里也不再细说,我们主要来说一下Java如何将异步调用转为同步。换句话说,就是需要在异步调用过程中,持续阻塞至获得调用结果。接下来将介绍5种Java并发编程中异步转同步的方法。
- 使用wait和notify方法
- 使用条件锁
- Future
- 使用CountDownLatch
- 使用CyclicBarrier
前置条件:构造一个异步调用
首先,写demo需要先写基础设施,这里是需要构造一个异步调用模型。异步调用类:
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class AsyncCall { private Random random = new Random(System.currentTimeMillis()); private ExecutorService tp = Executors.newSingleThreadExecutor(); public void call(AbstractBaseDemo demo) {
new Thread(() -> {
long res = random.nextInt(10);
try {
Thread.sleep(res * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
} demo.callback(res);
}).start();
} public Future<Long> futureCall() {
return tp.submit(() -> {
long res = random.nextInt(10); Thread.sleep(res * 1000);
return res;
});
} public void shutdown() {
tp.shutdown();
}
}
demo的基类:
public abstract class AbstractBaseDemo {
protected AsyncCall asyncCall = new AsyncCall();
public abstract void callback(long response);
public void call() {
System.out.println(Thread.currentThread().getName() + "发起调用");
asyncCall.call(this);
System.out.println(Thread.currentThread().getName() + "调用返回");
}
}
AbstractBaseDemo非常简单,里面包含一个异步调用类的实例,另外有一个call方法用于发起异步调用,当然还有一个抽象方法callback需要每个demo去实现的——主要在回调中进行相应的处理来达到异步调用转同步的目的。
一、使用wait和notify方法
这个方法其实是利用了锁机制,直接贴代码:
public class ObjectWaitLockDemo extends AbstractBaseDemo {
private final Object lock = new Object();
@Override
public void callback(long response) {
System.out.println(Thread.currentThread().getName() + "得到结果");
System.out.println(response);
System.out.println(Thread.currentThread().getName() + "调用结束");
synchronized (lock) {
lock.notifyAll();
}
}
public static void main(String[] args) {
ObjectWaitLockDemo demo = new ObjectWaitLockDemo();
demo.call();
synchronized (demo.lock) {
try {
demo.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "主线程内容");
}
}
没有使用同步操作的情况下,打印结果:
main发起调用
main调用返回
main主线程内容
Thread-0得到结果
7
Thread-0调用结束
而使用了同步操作后:
main发起调用
main调用返回
Thread-0得到结果
3
Thread-0调用结束
main主线程内容
二、使用条件锁
和方法一的原理类似:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockDemo extends AbstractBaseDemo { private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition(); @Override
public void callback(long response) {
System.out.println(Thread.currentThread().getName() + "得到结果");
System.out.println(response);
System.out.println(Thread.currentThread().getName() + "调用结束"); lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
} public static void main(String[] args) {
ReentrantLockDemo demo = new ReentrantLockDemo(); demo.call(); demo.lock.lock(); try {
demo.condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
demo.lock.unlock();
} System.out.println(Thread.currentThread().getName() + "主线程内容");
}
}
基本上和方法一没什么区别,只是这里使用了条件锁,两者的锁机制有所不同。
三、Future
使用Future的方法和之前不太一样,我们调用的异步方法也不一样。
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; public class FutureDemo { private AsyncCall asyncCall = new AsyncCall(); public Future<Long> call() {
Future<Long> future = asyncCall.futureCall(); asyncCall.shutdown(); return future;
} public static void main(String[] args) {
FutureDemo demo = new FutureDemo(); System.out.println(Thread.currentThread().getName() + "发起调用");
Future<Long> future = demo.call();
System.out.println(Thread.currentThread().getName() + "返回结果"); while (!future.isDone() && !future.isCancelled()); try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} System.out.println(Thread.currentThread().getName() + "主线程内容");
}
}
public void shutdown() {
tp.shutdown();
}
四、使用CountDownLatch
使用CountDownLatch或许是日常编程中最常见的一种了,也感觉是相对优雅的一种:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo extends AbstractBaseDemo {
private final CountDownLatch countDownLatch = new CountDownLatch(1);
@Override
public void callback(long response) {
System.out.println(Thread.currentThread().getName() + "得到结果");
System.out.println(response);
System.out.println(Thread.currentThread().getName() + "调用结束");
countDownLatch.countDown();
}
public static void main(String[] args) {
CountDownLatchDemo demo = new CountDownLatchDemo();
demo.call();
try {
demo.countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "主线程内容");
}
}
当然,这里和ObjectWaitLockDemo和ReentrantLockDemo中都一样,主线程中阻塞的部分,都可以设置一个超时时间,超时后可以不再阻塞。
五、使用CyclicBarrier
CyclicBarrier的情况和CountDownLatch有些类似:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier; public class CyclicBarrierDemo extends AbstractBaseDemo { private CyclicBarrier cyclicBarrier = new CyclicBarrier(2); @Override
public void callback(long response) {
System.out.println(Thread.currentThread().getName() + "得到结果");
System.out.println(response);
System.out.println(Thread.currentThread().getName() + "调用结束"); try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
} public static void main(String[] args) { CyclicBarrierDemo demo = new CyclicBarrierDemo(); demo.call(); try {
demo.cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
} System.out.println(Thread.currentThread().getName() + "主线程内容"); }
}
总结
综上,就是本次需要说的几种方法了。事实上,所有的方法都是同一个原理,也就是在调用的线程中进行阻塞等待结果,而在回调中函数中进行阻塞状态的解除。
Java并发(二)异步转同步的更多相关文章
- Java并发(二):基础概念
并发编程的第二部分,先来谈谈发布(Publish)与逸出(Escape); 发布是指:对象能够在当前作用域之外的代码中使用,例如:将对象的引用传递到其他类的方法中,对象的引用保存在其他类可以访问的地方 ...
- java并发编程基础——线程同步
线程同步 一.线程安全问题 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码.如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安 ...
- java并发编程:线程同步和锁
一.锁的原理 java中每个对象都有一个内置锁.当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this)有关的锁.获得一个对象的锁也称为获取锁,当程序运 ...
- Java线程(二):线程同步synchronized和volatile
上篇通过一个简单的例子说明了线程安全与不安全,在例子中不安全的情况下输出的结果恰好是逐个递增的(其实是巧合,多运行几次,会产生不同的输出结果),为什么会产生这样的结果呢,因为建立的Count对象是线程 ...
- Java并发编程,互斥同步和线程之间的协作
互斥同步和线程之间的协作 互斥同步 Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是 JVM 实现的 synchronized,而另一个是 JDK 实现的 ReentrantLo ...
- Java并发编程3-抽象同步队列AQS详解
AQS是AtractQueuedSynchronizer(队列同步器)的简写,是用来构建锁或其他同步组件的基础框架.主要通过一个int类型的state来表示同步状态,内部有一个FIFO的同步队列来实现 ...
- java多线程二之线程同步的三种方法
java多线程的难点是在:处理多个线程同步与并发运行时线程间的通信问题.java在处理线程同步时,常用方法有: 1.synchronized关键字. 2.Lock显示加锁. 3.信号量Se ...
- java CountDownLatch 控制异步和同步
应用场景举例: 执行A项目的方法,需要调用B项目.C项目.D项目的接口方法. 需求: 异步调用B.C.D项目的接口方法,且每个接口都调用结束后,A项目的方法才可以结束. 注:如果需要获取接口返回结果, ...
- java并发(二):初探syncronized
参考博客 Java多线程系列--"基础篇"04之 synchronized关键字 synchronized基本规则 第一条 当线程访问A对象的synchronized方法和同步块的 ...
- Java并发编程之Lock(同步锁、死锁)
这篇文章是接着我上一篇文章来的. 上一篇文章 同步锁 为什么需要同步锁? 首先,我们来看看这张图. 这是一个程序,多个对象进行抢票. package MovieDemo; public class T ...
随机推荐
- ServiceFramework作为Java Web框架都有哪些不错的设计
前言 最近需要开发一个纯API的项目,mlsql-cluster,从无到有,到最后完整的proxy功能开发完毕,只花了四个小时不到,自己不尽小感叹了一把 ServiceFramework的高效. 关于 ...
- C#设计模式六大原则概述
在面向对象的设计中,我们应当遵循以下六大原则,只有掌握了这些原则,才能更好的理解设计模式. 1:单一职责原则(Single Responsibility Principle 简称 :SRP) : 就一 ...
- 详解scss的继承、占位符和混合宏
1.继承和占位符 两者都是通过@extend来引用. 1.1 继承 一个已经存在的css样式类,可以被其他样式类继承. 例如,实现以下css样式: .btn, .btn--primary, .btn- ...
- C#特性:ConditionalAttribute
ConditionalAttribute类 msdn解释: 指示编译器应忽略方法调用或属性,除非已定义指定的条件编译符号. 命名空间: System.Diagnostics 语法: // // 摘要: ...
- 【读书笔记】iOS-“一心多用”利用多线程提升性能
iPhone将具有支持不同类型多线程API的能力,这些API包括:POSIX线程,NSObject,NSThread和NSOperation. iPhone操作系统是一个真正的抢占式,多任务操作系统, ...
- sqlserver每行都有累加,你怎么搞,窗口函数来帮忙!!!
在统计中我们经常会面临复杂的查询,例如累加,最近也遇到一个很好的例子,今天我们一起来看看: 有一个关于销售数据的视图,字段有:emptyid(员工编号),ordermonth( ...
- WRT 下 C++ wstring, string, String^ 互转
由于项目原因,需要引入C++. wstring 与 string 的互转研究了一段时间,坑主要在于使用下面这种方式进行转换,中文会乱码 wstring ws = L"这是一段测试文字&quo ...
- (网页)在SQL Server中为什么不建议使用Not In子查询(转)
转自博客园宋沄剑 英文名:CareySon : 在SQL Server中,子查询可以分为相关子查询和无关子查询,对于无关子查询来说,Not In子句比较常见,但Not In潜在会带来下面两种问题: ...
- [20171110]sql语句相同sql_id可以不同吗.txt
[20171110]sql语句相同sql_id可以不同吗.txt --//提一个问题,就是sql语句相同sql_id可以不同吗?--//使用dbms_shared_pool.markhot就可以做到. ...
- MVC model验证 获取验证错误信息
public static class ModelStateExtensions { /// <summary> /// 获取model验证错误信息 /// </summary> ...