该模式主要用于并行处理多个互不影响的请求,最后将结果汇总的业务。可以对多个不同的请求启动多个不同的线程,然后在线程中独立去获取想要的结果,等到真正需要使用该数据的时候才会获取到真正的结果。
下面是模拟的Future模式,(JDK已经提供了一些Future实现类,直接使用即可)
FutureClient.java
/**
* 核心处理类,对请求启用独立线程
* @author jliu10
*
*/
public class FutureClient{
public Data request(final String condition){
//创建一个数据包装对象
final FutureData futureData = new FutureData();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
//业务处理,请求数据
RealData realData = new RealData();
realData.request(condition);
//将真实的结果数据设置到Future数据包装对象中
futureData.setRealData(realData);
}
}).start();
//返回数据包装对象,此时返回的包装对象是虚拟的,因为在线程没有执行完成之前,即futureData.setRealData(realData);调用之前,该对象是没有真实数据的
return futureData;
}
}
FutureData.java
/**
* Future数据包装对象,利用notify和wait来阻塞请求,以确保最终能够获取到数据
* @author jliu10
*/
public class FutureData implements Data{
private RealData realData;//真实数据
private boolean isReady = false;//是否已经加载成功
/**
* 设置真实数据
* @param realData
*/
public synchronized void setRealData(RealData realData){
if( isReady ){ //如果已经设置结果了,直接返回
return;
}
//设置结果对象
this.realData = realData;
isReady = true;
//唤醒获取数据的线程
System.out.println("数据准备完成,唤醒等待数据的线程...");
notify();
}
@Override
public synchronized String getRequest() {
// TODO Auto-generated method stub
while(!isReady){//如果还没有设置结果,进入等待,等待结果设置成功
try {
System.out.println("数据还没有准备好,等待数据...");
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return this.realData.getRequest();
}
}
RealData.java
/**
* 真实数据结果对象
* @author jliu10
*/
public class RealData implements Data {
private String result = null;
/**
* 请求数据,真实场景中可能需要请求数据等操作,此处模拟耗时5s
* @param condition
*/
public void request(String condition){
System.out.println("数据库获取数据,数据条件是:" + condition);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
result = "我是返回结果数据.";
System.out.println("获取数据成功.");
}
@Override
public String getRequest() {
// TODO Auto-generated method stub
return this.result;
}
}
Data.java
/**
* 数据接口,需事先数据返回方法
* @author jliu10
*/
public interface Data {
String getRequest();
}
Main.java
/**
* 模拟主函数
* @author jliu10
*/
public class Main {
public static void main(String[] args) {
//请求
FutureClient client = new FutureClient();
//future处理类返回虚拟结果
Data data = client.request("请求数据...");
System.out.println("请求发送成功...");
System.out.println("处理其他逻辑...");
try {
Thread.sleep(2000);//模拟耗时2秒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("使用请求的数据...");
String realData = data.getRequest();
System.out.println("数据结果是:" + realData);
}
}
result
请求发送成功...
处理其他逻辑...
数据库获取数据,数据条件是:请求数据...
使用请求的数据...
数据还没有准备好,等待数据...
获取数据成功.
数据准备完成,唤醒等待数据的线程...
数据结果是:我是返回结果数据.
常用的并行计算模式。他的核心思想是系统由两类进程协作工作:Master进程和Worker进程。Master负责接收和分配任务,Worker负责处理子任务。当各个Worker子进程处理完成后,会将结果返回给Master,由Master做归纳和总结。其好处是能将一个大任务分解成若干个小任务,并行执行,从而提高系统的吞吐量。
模拟Master-Worker模式
Master.java
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* 1、使用高性能的并发队列来承装所有的任务,推荐:ConcurrentLinkedQueue
* 2、使用一个容器来承装所有的Worker对象,不需要并发
* 3、使用并发类容器来接收work的返回结果,为了能够查寻到是哪一个work的结果,推荐使用Map类容器
* @author jliu10
*
*/
public class Master {
//高性能无锁并发队列,用来接收任务
private final ConcurrentLinkedQueue jobsQueue = new ConcurrentLinkedQueue<>();
//用来存放所有的work
private Map workersMap = new HashMap();
//用来承装每一个worker并发处理任务的结果集
private final ConcurrentHashMap resultsMap = new ConcurrentHashMap();
public Master(Worker worker, int workerCount) {
//将master的任务队列和结果集传入worker
worker.setMasterJobsQueue(this.jobsQueue);
worker.setMasterResultsMap(this.resultsMap);
for (int i = 0; i < workerCount; i++) {
//key表示每一个worker的名字, value表示线程执行对象
workersMap.put("worker-" + i, new Thread(worker));
}
}
/**
* 提交方法
*/
public void submit(Job job){
this.jobsQueue.add(job);
}
/**
* 执行方法,启动Master应用程序,让所有的Worker开始工作
*/
public void execute(){
for(Map.Entry me : workersMap.entrySet() ){
me.getValue().start();
}
}
/**
* 判断线程是否执行完毕
* @return
*/
public boolean isComplete() {
// TODO Auto-generated method stub
for(Map.Entry me : workersMap.entrySet() ){
if(me.getValue().getState() != Thread.State.TERMINATED){
return false;
}
}
return true;
}
public int getResult() {
// TODO Auto-generated method stub
int ret = 0;
for(Map.Entry me : resultsMap.entrySet()){
ret += (Integer)me.getValue();
}
return ret;
}
}
Worker.java
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* 1、每一个Worker为一个线程,所以必须要实现Runnable接口
* 2、每一个Worker必须要有Master的ConcurrentLinkedQueue的引用,用于从中获取任务
* 3、每一个Worker必须要有Master的ConcurrentHashMap的引用,用来将Worker的处理结果返回给Master
* @author jliu10
*
*/
public class Worker implements Runnable {
private ConcurrentLinkedQueue masterJobsQueue;
private ConcurrentHashMap masterResultsMap;
public void setMasterJobsQueue(ConcurrentLinkedQueue masterJobsQueue) {
this.masterJobsQueue = masterJobsQueue;
}
public void setMasterResultsMap(
ConcurrentHashMap masterResultsMap) {
this.masterResultsMap = masterResultsMap;
}
/**
*子类重写该方法用以实现自己的业务逻辑
*/
public Object handle(Job job){
return null;
}
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
//从队列中取一个任务开始执行
Job job = this.masterJobsQueue.poll();
//如果队列中没有任务了,则退出处理
if( null == job ){
break;
}
Object object = this.handle(job);
this.masterResultsMap.put(Integer.toString(job.getId()), object);
}
}
}
MyWorker.java
public class MyWorker extends Worker {
/**
* 处理业务逻辑的方法
*/
@Override
public Object handle(Job job) {
Object output = null;
// TODO Auto-generated method stub
try {
//表示处理job任务,可能是数据的加工,也可能是操作数据库,此处用休眠做模拟,处理结果为output
Thread.sleep(500);
output = job.getPrice();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return output;
}
}
生产者和消费者也是一个非常经典的多线程模式,我们在实际的开发中应用非常广泛的思想理念。在生成-消费模式中:通常由两类线程,即若干个生产者的线程和若干个消费者的线程。生产者线程负责提交用户请求,消费者线程则负责具体处理生产者提交的任务,在生产者和消费者之间通过共享内存缓存进行通信。
Provider.java
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 生产者
* @author jliu10
*/
public class Provider implements Runnable{
//公共缓存区
private BlockingQueue queue;
//是否继续运行
private volatile boolean isRunning = true;
//静态变量,用来模拟数据
private static AtomicInteger count = new AtomicInteger(0);
private static Random r = new Random();
public Provider(BlockingQueue queue) {
// TODO Auto-generated constructor stub
this.queue = queue;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(isRunning){
try {
//模拟生产数据,可以是从数据库获取
Thread.sleep(r.nextInt(1000));
int id = count.incrementAndGet();
Data data = new Data(Integer.toString(id), "数据-" + id);
System.out.println("当前线程:" + Thread.currentThread().getName() + " ,获取了数据,id为:" + id + ", 进行装载数据到缓冲区...");
//将数据插入到公共缓冲区,2s超时
if(!this.queue.offer(data, 2, TimeUnit.SECONDS)){
System.out.println("提交数据到缓冲区失败...");
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void stop() {
// TODO Auto-generated method stub
this.isRunning = false;
}
}
Consumer.java
import java.util.Random;
import java.util.concurrent.BlockingQueue;
/**
* 消费者
* @author jliu10
*/
public class Consumer implements Runnable{
//公共缓存区
private BlockingQueue queue;
private static Random r = new Random();
public Consumer(BlockingQueue queue) {
// TODO Auto-generated constructor stub
this.queue = queue;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
//从公共缓冲区获取数据进行消费
Data data = this.queue.take();
//模拟消费数据
Thread.sleep(r.nextInt(1000));
System.out.println("当前消费线程:" + Thread.currentThread().getName() + ", 消费成功,消费数据id:" + data.getId());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Main.java
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
/**
* 主测试函数
* @author jliu10
*/
public class Main {
public static void main(String[] args) {
//内存缓冲区, LinkedBlockingQueue内部实现了读写分离锁,可以是读写完全并行
BlockingQueue blockingQueue = new LinkedBlockingQueue(10);
//生产者
Provider p1 = new Provider(blockingQueue);
Provider p2 = new Provider(blockingQueue);
Provider p3 = new Provider(blockingQueue);
//消费者
Consumer c1 = new Consumer(blockingQueue);
Consumer c2 = new Consumer(blockingQueue);
Consumer c3 = new Consumer(blockingQueue);
//创建线程池运行
ExecutorService cachPool = Executors.newCachedThreadPool();
cachPool.execute(p1);
cachPool.execute(p2);
cachPool.execute(p3);
cachPool.execute(c1);
cachPool.execute(c2);
cachPool.execute(c3);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
p1.stop();
p2.stop();
p3.stop();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}