并行设计模式属于设计优化的一部分,它是对一些常见的多线程结构的总结和抽象,与串行相比,并行程序的结构通常更为复杂,因此合理的使用并行模式在多线程开发中更具有意义,这里主要介绍future和Master-Worker两种模式。
future模式
future有点像商品订单。比如在网购时,当看中某一件商品时,就可以提家订单,当订单处理完成后我们在家等着送货上门即可。或者更形象的说是ajax请求的时候,页面是异步进行后台处理的,用户无需一直等待请求的处理结果,可以继续浏览或操作其他内容
概述:future模式会创建一个子线程去完成相应的任务,然后将处理结果返回给主线程main,在子线程处理数据的过程中,主线程可以继续做其他的事。
场景:future模式很适合处理耗时很长的业务逻辑,可以有效的减少系统的响应时间,提高系统的吞吐量。
演示代码 :
创建Data接口:
package com.company;
public interface Data {
String getRequest();
}
实际需要的数据:
package com.company;
public class RealData implements Data{
private String resultStr;
public RealData(String query) {
System.out.println("根据查询条件"+query+"正在进行查询");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结果查询完毕");
this.resultStr = "我是查询结果";
}
@Override
public String getRequest() {
return resultStr;
}
}
装载真实数据的伪数据(可以看做网购时的快递员):
package com.company;
public class FutureData implements Data {
private RealData realData;
private boolean isReady = false;
public synchronized void setRealData(RealData realData){
if(isReady){
return;
}
this.realData = realData;
isReady = true;
this.notify();
}
@Override
public synchronized String getRequest() {
if(!isReady){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return this.realData.getRequest();
}
}
处理数据的端口,也就是创建线程的端口
package com.company;
public class FutureClient {
public Data request(String query){
FutureData f = new FutureData();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
RealData re = new RealData(query);
f.setRealData(re);
}
});
t.start();
return f;
}
}
主函数:
package com.company;
public class Main {
// 假设这是一个租房子的过程,main函数代表你这一天的行程
public static void main(String[] args) {
// 1.你来到一个中介公司
FutureClient fc = new FutureClient();
// 2.你告诉他你想要一个房子,(这里data就是房子的一个抽象)。中介公司马上告诉你,放心吧
// 按你说的要求(查找条件)我们肯定能找到
// 3.中介公司会把你找房子这个事安排给他们的员工中介经纪人——futureData
// 4. 一旦房子找到,这个中介就会记录房子的信息或拍照,然后马上通知你,给你发微信(notify)
Data da = fc.request("查询条件");
// 5.因为这个中介公司百分百靠谱,已经给你个合同了(拿到返回值Data),所以你认为你房子已经找到了,并对你女朋友说,房子找好了,我们出去玩、吃饭、看电影都可以,什么都不耽误
System.out.println("发送请求成功");
// 6.一旦中介找到房子,你微信就接到了房子照片和地址还有房间门密码,
System.out.println("获得了结果"+da.getRequest());
}
}
运行结果:
发送请求成功
根据查询条件查询条件正在进行查询
结果查询完毕
获得了结果我是查询结果
整个执行过程:
主函数创建获取数据的客户端FutureClient并将查询条件传入。FutureClient端马上返回一个可以封装(但是暂时还未封装)realyData对象的的FutureData。同时创建一个线程A去处理真正的请求(根据查询条件查询出一个realyData)。同时继续向下执行到 da.getRequest()方法时,这个方法时同步方法,拿到FutureData对象后验证是否已经完成了realyData的装载,如果没完成则马上执行wait方法等待。直到线程A通过这个查询条件查出realyData后将结果set给FutureData,并通知等待着的线程,我已经获取到了realyData。这是等待的线程da.getRequest就获取到了结果。
JDK中已经提供了Future模式的封装,使用方式演示
CallAble和Future
1.此模式非常适合处理很耗时,很长的业务逻辑时使用,可以有效减小系统的响应时间,提高系统的吞吐量FutureTask构成函数传入的实例对象一定要实现callAble,重写call方法。
2.同时说一下submit和execute的区别,1:submit可以传入实现callAble接口的对象;2:submit有返回值,返回future对象
package com.company;
import java.util.concurrent.*;
public class UserFuture implements Callable<String>{
//房子地址
private String query;
public UserFuture(String query) {
this.query = query;
}
//经纪人找房的过程
@Override
public String call() throws Exception {
//根据条件找房子
Thread.sleep(3000);
System.out.println();
String result = "根据条件"+ query +"找到的结果是:xxx小区1号楼A单元401";
return result;
}
public static void main(String[] args) throws Exception{
//告诉中介我要找什么样的房子
String query = "高大上";
//创建了一个找房子通知书,告诉中介我要找房子的计划
System.out.println("创建了一个找房子通知书,告诉中介我要找房子的计划");
FutureTask<String> f1 = new FutureTask<String>(new UserFuture(query));
FutureTask<String> f2 = new FutureTask<String>(new UserFuture(query));
//准备同时让两个中介去帮你找房子
System.out.println("准备同时让两个中介去帮你找房子");
ExecutorService executorService = Executors.newFixedThreadPool(2);
//发通知,并得到了中介公司的恢复,表示能找到,线上签合同了
Future da11 = executorService.submit(f1);
Future da12 = executorService.submit(f2);
System.out.println("答应一定能找到,并且,找到房子后会给我发微信");
//咱们去吃饭
System.out.println("吃好吃的,刷羊肉、烧烤、看电影");
Thread.sleep(3000);
//第一家中介找到了给我通知
String result1 = f1.get();
System.out.println(result1);
//第二家中介找到了给我通知
String result2 = f2.get();
System.out.println(result2);
executorService.shutdown();
}
}
运行结果:
主线程运行到future.get()的时候就阻塞住了,一直等到任务执行完毕,获取的返回值,表示任务执行完毕,主线程就可以跟进执行进度后继续执行
futureTask 的get方法异步调用call方法的执行结果,直到执行完成后get()方法才能获取结果然后主线程才能继续执行。get方法阻塞的是每个执行任务的线程的阻塞,而不是主线程的阻塞
创建了一个找房子通知书,告诉中介我要找房子的计划
准备同时让两个中介去帮你找房子
答应一定能找到,并且,找到房子后会给我发微信
吃好吃的,刷羊肉、烧烤、看电影
根据条件高大上找到的结果是:xxx小区1号楼A单元401
根据条件高大上找到的结果是:xxx小区1号楼A单元401
Master—Worker模式
Master-Worker模式是常用的并行计算模式,它的核心思想是系统有两类进程协作工作,Master负责接收和分配任务,Worker负责处理子任务。当各个Worker进程处理完成后,会将结果返回给Master,由Master做总结归纳。其好处是将一个大的任务分解成若干个小任务并行执行,从而提高系统的吞吐量。
task,要处理的数据
package com.company.masterWorker;
public class Task {
private Integer id;
private int price;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
Worker 代码
package com.company.masterWorker;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class Worker implements Runnable {
private ConcurrentLinkedQueue<Task> workQueue;
private ConcurrentHashMap<String,Object> workMap;
public void setWorkQueue(ConcurrentLinkedQueue<Task> workQueue) {
this.workQueue = workQueue;
}
public void setWorkMap(ConcurrentHashMap<String, Object> workMap) {
this.workMap = workMap;
}
@Override
public void run() {
while (true){
Task ta = this.workQueue.poll();
if(ta == null ) {
break;
}
this.workMap.put(String.valueOf(ta.getId()),MyWorker.handler(ta));
}
}
public static Object handle(Task ta){
return null;
}
}
MyWorker代码
package com.company.masterWorker;
public class MyWorker extends Worker {
public static Object handler(Task task){
Object obj = null;
try {
Thread.sleep(500);
obj = task.getPrice();
} catch (InterruptedException e) {
e.printStackTrace();
}
return obj;
}
}
master控制代码
package com.company.masterWorker;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class Master {
//放任务的队列
ConcurrentLinkedQueue<Task> queue = new ConcurrentLinkedQueue();
//放任务
Map<String,Thread> workerHashMap = new HashMap<>();
//放结果
ConcurrentHashMap<String,Object> workResult = new ConcurrentHashMap<>();
public Master(Worker worker,int workCount ) {
worker.setWorkQueue(this.queue);
worker.setWorkMap(this.workResult);
for(int i =0; i< workCount ; i++){
workerHashMap.put(String.valueOf(i), new Thread(worker));
}
}
//向队列中放任务
public void submit(Task task){
this.queue.add(task);
}
//执行所有的worker
public void execute(){
for(Map.Entry<String,Thread> w : workerHashMap.entrySet() ){
w.getValue().start();
}
}
//判断是否执行完毕
public boolean isComplete(){
for(Map.Entry<String,Thread> w : workerHashMap.entrySet() ){
if(w.getValue().getState() != Thread.State.TERMINATED){
return false;
}
}
return true;
}
//计算结果
public int getResult(){
Integer resut = 0;
for (Map.Entry<String, Object> re : workResult.entrySet()){
resut += (Integer) re.getValue();
}
return resut;
}
}
主线程代码
package com.company.masterWorker;
import java.math.BigDecimal;
import java.util.Random;
public class Main {
public static void main(String[] args) {
System.out.println("本机可用processor数量"+Runtime.getRuntime().availableProcessors());
Master ma = new Master(new MyWorker(),Runtime.getRuntime().availableProcessors());
Random r = new Random();
for(int i=0;i<100;i++){
Task ta =new Task();
ta.setId(i);
ta.setPrice(r.nextInt(1000));
ma.submit(ta);
}
ma.execute();
BigDecimal start = new BigDecimal( System.currentTimeMillis());
while (true){
if(ma.isComplete()){
BigDecimal endTime = new BigDecimal( System.currentTimeMillis());
Integer pri = ma.getResult();
System.out.println("最终结果:"+pri+"执行了"+(endTime.subtract(start)).divide(new BigDecimal(1000)) +"秒");
break;
};
}
}
}
运行结果:这里为了保证时间计算的精确度,我使用了Bigdecimal,实际完全不必要,用long类型就可以了。
执行过程解释:
master中声明了几个重要的共享容器。ConcurrentLinkedQueue 用来装要被执行的任务。然后ConcurrentHashMap中以key-value的形式放worker执行任务后的结果。最后当任务全部执行完毕,再由master处理放在ConcurrentHashMap中的结果。这里判断是否执行完毕的方法可以优化,换成countDownLanch计数的方式,或者使用线程池去执行,最后判断线程池是否执行完毕。
生产者和消费者
生产者和消费者也是一个非常经典的多线程模式,我们在实际开发中应用非常广泛的思想理念。在生产者-消费者中通常有两类线程,若干个生产者和若干个消费者的线程。生产者负责提交用户请求,消费者负责处理生产者提交的具体任务。在生产者和消费者之间通过共享内存缓存区进行通信
数据:Data
package com.company.producerConsumer;
/**
* Created by BaiTianShi on 2018/8/22.
*/
public class Data {
private String id;
private String name;
public Data(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "{id:"+id+ ",name:"+ name +"}";
}
}
数据生产者 producer:
package com.company.producerConsumer;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by BaiTianShi on 2018/8/22.
*/
public class Producer implements Runnable {
private BlockingQueue<Data> queue;
private static AtomicInteger count = new AtomicInteger();
private static volatile boolean isRunning= true;
public Producer(BlockingQueue<Data> queue) {
this.queue = queue;
}
private static Random r = new Random(1000);
@Override
public void run() {
while (isRunning){
try {
int id = count.incrementAndGet();
Data da = new Data(String.valueOf(id),"数据"+id);
boolean addResult = false;
addResult = queue.offer(da,2, TimeUnit.SECONDS);
System.out.println("当前线程"+Thread.currentThread().getName()+"获取了数据id为"+id+"加入到队列中");
if(!addResult){
System.out.println("线程"+Thread.currentThread().getName()+"中,id为"+id+"数据加入队列失败");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void stop(){
isRunning = false;
}
}
数据消费者 conumer
package com.company.producerConsumer;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Created by BaiTianShi on 2018/8/22.
*/
public class Consumer implements Runnable {
private BlockingQueue<Data> queue;
public Consumer(BlockingQueue<Data> queue) {
this.queue = queue;
}
private static Random r = new Random();
@Override
public void run() {
while (true){
try {
Data da = this.queue.take();
Thread.sleep(r.nextInt(1000));
System.out.println("当前线程消费:"+Thread.currentThread().getName()+",成功消费的数据id是:"+da.getId());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
主线程控制:
package com.company.producerConsumer;
import com.company.masterWorker.Worker;
import java.util.concurrent.*;
/**
* Created by BaiTianShi on 2018/8/22.
*/
public class Master {
public static void main(String[] args) {
BlockingQueue<Data> queue = new LinkedBlockingQueue<>(10);
Producer p1 = new Producer(queue);
Producer p2 = new Producer(queue);
Producer p3 = new Producer(queue);
Consumer c1 = new Consumer(queue);
Consumer c2 = new Consumer(queue);
Consumer c3 = new Consumer(queue);
ExecutorService se = Executors.newCachedThreadPool();
se.execute(p1);
se.execute(p2);
se.execute(p3);
se.execute(c1);
se.execute(c2);
se.execute(c3);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
p1.stop();
p2.stop();
p3.stop();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}