关于多线程和线程安全相关讨论(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/bloodylzy/article/details/79139891

二:并行程序设计模式

 

有时候我们会利用并行线程的方式来完成我们的业务要求,专业来说有以下模式:

1.Future模式

2.Master-Worker模式

3.Guarded Suspension 模式

4.不变模式

5.生产者与消费者模式

 

 

本人才疏学浅,讲不出太多东西,这里就是对一个模式所对应的场景做一个总结,然后从代码层面做一个例子。

 

 

1.Future模式:

我们有时候会预见这种情况,我们要处理两个业务,但是这两个业务相互没有关联,我们需要的仅仅只是他们结果。

简单一点,我们要对ainsert十万条数据,对bupdate十万条数据,如果按照顺序执行下来,会消耗巨大的时间。而我们仅仅只需要知道insertupdate分别是否成功。

这里我们就可以在执行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模式:

有时候会遇见这种情况,短时间内有大量的请求传入到服务器,但是服务器无法一下子全部的处理完成,但是对于传入的请求,我们也无法舍弃。

关于这个模式的场景模拟是这样的:我先起10Client线程,每个线程内实现发送10个请求的操作,然后我再起10Service线程,去处理这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("消费者停止消费");
	}

}





猜你喜欢

转载自blog.csdn.net/bloodylzy/article/details/79139891