互联网技术11——future模式和Master-Worker模式

 并行设计模式属于设计优化的一部分,它是对一些常见的多线程结构的总结和抽象,与串行相比,并行程序的结构通常更为复杂,因此合理的使用并行模式在多线程开发中更具有意义,这里主要介绍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();
        }

    }

}

 

 

 

猜你喜欢

转载自blog.csdn.net/qq_28240551/article/details/81880405