二:并行程序设计模式
有时候我们会利用并行线程的方式来完成我们的业务要求,专业来说有以下模式:
1.Future模式
2.Master-Worker模式
3.Guarded Suspension 模式
4.不变模式
5.生产者与消费者模式
本人才疏学浅,讲不出太多东西,这里就是对一个模式所对应的场景做一个总结,然后从代码层面做一个例子。
1.Future模式:
我们有时候会预见这种情况,我们要处理两个业务,但是这两个业务相互没有关联,我们需要的仅仅只是他们结果。
简单一点,我们要对a表insert十万条数据,对b表update十万条数据,如果按照顺序执行下来,会消耗巨大的时间。而我们仅仅只需要知道insert和update分别是否成功。
这里我们就可以在执行insert的之前,先起一个线程去执行update的操作,这样,我们就能用一个业务的时间去完成两个业务。而我们需要知道的只是,另起的线程是否成功;
JDK提供了Future的一种实现方式
package com.liu.javathread.future;
import java.util.List;
import java.util.concurrent.Callable;
public class Future implements Callable<String> {
private List<Integer> updateList;
public Future(List<Integer> updateList) {
super();
this.updateList = updateList;
}
@Override
public String call() throws Exception {
try {
for (int update : updateList) {
// 这里做update操作
System.out.println("updateList:" + update);
}
} catch (Exception e) {
return "error";
}
return "ok";
}
}
package com.liu.javathread.future;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public class FutureMain {
public static void main(String[] args) throws InterruptedException, ExecutionException {
List<Integer> addList = new ArrayList<>();
List<Integer> updateList = new ArrayList<>();
for(int i=0;i<10000;i++){
updateList.add(i);
addList.add(i);
}
System.out.println("准备完成");
FutureTask<String> future = new FutureTask<>(new Future(updateList));
// 创建一个线程数为1的线程池
ExecutorService executor = Executors.newFixedThreadPool(1);
executor.submit(future);
for(Integer i:addList){
System.out.println("addList:"+i);
}
System.out.println(future.get());
}
}
总结地说:Future模式的核心思想就是多个无关的业务同时执行,充分利用服务器资源
2.Master-Worker模式:
有时候会预见这种情况,我们要处理一个数据量非常庞大的业务,但是要求花费的时间要很短。
举个例子,我们要计算1~100的立方和,如果我们做一个for循环去计算,时间肯定比我们去起一百个线程,分别去计算每一个数字的立方然后加起来的时间要断。
从这个例子出发,我们的主线程(称为Master)接到了要求计算1~100的立方和的业务请求,然后将这个业务请求分成100个小的子线程(称为Worker),这些Worker计算完成后,将他们的计算结果汇总到Master,最后Master将最后的结果输出。
这里我们甚至可以参考一下网上的一个例子,对于上述场景,我们的Master甚至可以不需要等到所有的计算完成,就可以慢慢地得到result
package com.liu.javathread.masterworker;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class Master {
protected Queue<Integer> workQueue = new ConcurrentLinkedQueue<>();
protected Map<String,Integer> resultMap = new ConcurrentHashMap<>();
protected Map<String,Thread> threadMap = new HashMap<>();
/**
* 判断所有的Worker是否已经完成
* @date 2017年11月13日 上午9:59:37
* @author: liuzy
* @return
*/
public boolean isComplete(){
for(Map.Entry<String, Thread> entry:threadMap.entrySet()){
//System.out.println(entry.getValue().getName()+" : "+entry.getValue().getState());
if(entry.getValue().getState() != Thread.State.TERMINATED){
return false;
}
}
return true;
}
public Master(Worker worker,Integer countWork) {
worker.setWorkQueue(workQueue);
worker.setResultMap(resultMap);
for(int i=0;i<countWork;i++){
threadMap.put(Integer.toString(i), new Thread(worker, Integer.toString(i)));
}
}
public void submit(Integer input){
workQueue.add(input);
}
public Map<String, Integer> getResultMap() {
return resultMap;
}
public void exec(){
for(Map.Entry<String, Thread> entry:threadMap.entrySet()){
entry.getValue().start();
}
}
}
package com.liu.javathread.masterworker;
import java.util.Map;
import java.util.Queue;
public class Worker implements Runnable{
protected Queue<Integer> workQueue;
protected Map<String,Integer> resultMap;
public void setWorkQueue(Queue<Integer> workQueue) {
this.workQueue = workQueue;
}
public void setResultMap(Map<String, Integer> resultMap) {
this.resultMap = resultMap;
}
private Integer handle(Integer input){
return input*input*input;
}
@Override
public void run() {
while(true){
Integer put = workQueue.poll();
if(put == null){
break;
}
resultMap.put(put.toString(), handle(put));
}
}
}
package com.liu.javathread.masterworker;
import java.util.Map;
import java.util.Set;
public class MasterWorkerMain {
public static void main(String[] args) {
Master master = new Master(new Worker(), 5);
for(int i=1;i<101;i++){
master.submit(i);
}
master.exec();
int result = 0;
Map<String,Integer> resultMap = master.getResultMap();
while(resultMap.size() >0 || !master.isComplete()){
Set<String> keys = resultMap.keySet();
String key = null;
for(String k: keys){
key = k;
break;
}
Integer i = null;
if(key != null){
i = (Integer)resultMap.get(key);
}
if(i != null){
result += i;
}
if(key != null){
resultMap.remove(key);
}
if(key != null&&i != null){
System.out.println("key:"+key+" ,i:"+i+" ,result="+result);
}
}
System.out.println(result);
}
}
总结地说:MasterWorker模式就是将一整个复杂的业务逻辑分解成多个,利用多个线程去分别计算,最后汇总得到
3.Guarded Suspension模式:
有时候会遇见这种情况,短时间内有大量的请求传入到服务器,但是服务器无法一下子全部的处理完成,但是对于传入的请求,我们也无法舍弃。
关于这个模式的场景模拟是这样的:我先起10个Client线程,每个线程内实现发送10个请求的操作,然后我再起10个Service线程,去处理这100个请求。
基本思路是将request请求存放到队列之中,让多个线程去分别去队列中取得request,然后去处理他们。
package com.liu.javathread.gs;
/**
* 模拟一个请求
* @Title: Request.java
* @Package: com.cnwisdom.javathread.gs
* 2017年11月13日 上午11:45:59
* @author: liuzy
*
*/
public class Request {
private String name;
public Request(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Request [name=" + name + "]";
}
}
package com.liu.javathread.gs;
import java.util.concurrent.LinkedBlockingQueue;
public class RequestQueue {
private LinkedBlockingQueue<Request> requestQueue = new LinkedBlockingQueue<>();
public Request getRequest(){
try {
return requestQueue.take();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
/**
*
* @date 2017年11月13日 下午3:13:58
* @author: liuzy
* @param request
*/
public void addRequest(Request request){
try {
requestQueue.put(request);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*private LinkedList<Request> requestQueue = new LinkedList<>();
public synchronized Request getRequest(){
while(requestQueue.size() == 0){
try {
wait();
} catch (Exception e) {
// TODO: handle exception
}
}
return requestQueue.remove();
}
public synchronized void addRequest(Request request){
requestQueue.add(request);
notifyAll();
}
*/
}
package com.liu.javathread.gs;
/**
* 服务器端接收线程
* @Title: ServiceThread.java
* @Package: com.cnwisdom.javathread.gs
* 2017年11月13日 下午12:40:36
* @author: liuzy
*
*/
public class ServiceThread extends Thread{
private RequestQueue queue;
public ServiceThread(String name, RequestQueue queue) {
super(name);
this.queue = queue;
}
@Override
public void run(){
while(true){
Request request = queue.getRequest();
try {
// 相当于处理请求
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" handles "+request);
}
}
}
package com.liu.javathread.gs;
/**
* 客户端请求线程
* @Title: ClientThread.java
* @Package: com.cnwisdom.javathread.gs
* 2017年11月13日 下午12:40:22
* @author: liuzy
*
*/
public class ClientThread extends Thread{
private RequestQueue requestQueue;
public ClientThread(String name, RequestQueue requestQueue) {
super(name);
this.requestQueue = requestQueue;
}
@Override
public void run() {
for(int i=0;i<10;i++){
Request request = new Request("RequestID: "+i+" Thread_Name:"+Thread.currentThread().getName());
requestQueue.addRequest(request);
try {
// ServiceThread那里模拟处理请求时,线程sleep时间为1000,这里设置时间短,说明请求的速度大于处理的速度
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("ClientThread Name is :"+ Thread.currentThread().getName());
}
System.out.println(Thread.currentThread().getName()+" 发送请求完毕");
}
}
package com.liu.javathread.gs;
/**
*
* @Title: GSMain.java
* @Package: com.cnwisdom.javathread.gs
* 2017年11月13日 下午3:05:34
* @author: liuzy
*
*/
public class GSMain {
/**
* 具体我们可以看到,我们有10个客户端,10个服务端,客户端总共发了100个request,这100个request全部被requestQueue这个队列接收存储,然后这10个服务端会依次去队列中取得request请求,然后去处理
*
* @date 2017年11月13日 下午3:17:13
* @author: liuzy
* @param args
*/
public static void main(String[] args) {
RequestQueue requestQueue = new RequestQueue();
for(int i=0;i<10;i++){
new ServiceThread("ServiceThread"+i, requestQueue).start();
}
for(int i=0;i<10;i++){
new ClientThread("ClientThread"+i, requestQueue).start();
}
}
}
总结地说:这种模式的核心思路是将多个相同的业务通过队列的方式存储起来,然后多线程并发去处理
4.不变模式:
这个模式,我也不是很懂,书面表述如下:
1.当对象创建以后,其内部状态和数据不再发生任何变化
2.对象需要被共享,被多线程频繁访问
希望大家能指出不变模式和只读属性的区别
在Java中,最常用的不变模式的代表就是String。
5.生产者和消费者:
这个模式,大概是我们最常见的模式。从我个人感觉来说,我并不是很能分清楚生产者和消费者模式与Guarded Suspension模式,对于我而言,好像都是对方发过来的过多的请求,我无法立刻全部解决,所以我需要一个缓冲区(大多时候是一个堵塞队列),将来不及处理的东西有地方放置,接着我依次解决这些请求。
package com.liu.javathread.producerconsumer;
public final class Data {
private final int intData;
public Data(int intData) {
super();
this.intData = intData;
}
public int getIntData() {
return intData;
}
@Override
public String toString() {
return "Data [intData=" + intData + "]";
}
}
package com.liu.javathread.producerconsumer;
import java.text.MessageFormat;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
/**
* 消费者
*
* @Title: Consumer.java
* @Package: com.cnwisdom.javathread.producerconsumer 2017年11月13日 下午4:46:01
* @author: liuzy
*
*/
public class Consumer implements Runnable {
private BlockingQueue<Data> queue;
private static final int SLEEPTIME = 1000;
public Consumer(BlockingQueue<Data> queue) {
super();
this.queue = queue;
}
@Override
public void run() {
System.out.println("start Consumer id=" + Thread.currentThread().getId());
Random random = new Random();
try {
// 消费者永远在消费
while (true) {
Data data = queue.take();
if (data != null) {
int result = data.getIntData() * data.getIntData();
System.out
.println(MessageFormat.format("{0}*{1}={2}", data.getIntData(), data.getIntData(), result));
Thread.sleep(random.nextInt(SLEEPTIME));
}
}
} catch (Exception e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}
package com.liu.javathread.producerconsumer;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 生产者
* @Title: Producer.java
* @Package: com.cnwisdom.javathread.producerconsumer
* 2017年11月13日 下午4:45:51
* @author: liuzy
*
*/
public class Producer implements Runnable{
private volatile boolean isRunning = true;
private BlockingQueue<Data> queue;
private static AtomicInteger count = new AtomicInteger();
private static final Integer SLEEPTIME = 1000;
private static final Integer OFFTIME = 2;
public Producer(BlockingQueue<Data> queue) {
super();
this.queue = queue;
}
@Override
public void run() {
Data data = null;
Random random = new Random();
System.out.println("start : "+Thread.currentThread().getId());
try {
while(isRunning){
Thread.sleep(random.nextInt(SLEEPTIME));
data = new Data(count.incrementAndGet());
if(!queue.offer(data, OFFTIME, TimeUnit.SECONDS)){
System.err.println("failed to put data:"+data);
}
}
} catch (Exception e) {
e.printStackTrace();
// 中断本线程
Thread.currentThread().interrupt();
}
}
public void stop(){
isRunning = false;
}
}
package com.liu.javathread.producerconsumer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
public class PcMain {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<Data> queue = new LinkedBlockingQueue<>(10);
// 创建三个生产者
Producer producer1 = new Producer(queue);
Producer producer2 = new Producer(queue);
Producer producer3 = new Producer(queue);
// 创建三个消费者
Consumer consumer1 = new Consumer(queue);
Consumer consumer2 = new Consumer(queue);
Consumer consumer3 = new Consumer(queue);
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(producer1);
executor.submit(producer2);
executor.submit(producer3);
executor.submit(consumer1);
executor.submit(consumer2);
executor.submit(consumer3);
Thread.sleep(5000);
producer1.stop();
producer2.stop();
producer3.stop();
System.out.println("生产者已经停止生产了");
Thread.sleep(8000);
executor.shutdown();
System.out.println("消费者停止消费");
}
}