我所理解的并发#2:两种策略

之前的文章说到,运行时,程序=代码+数据。那么并发编程就可以有两种策略,代码并发和数据并发。

代码并发

代码并发的前提是,我们的代码,准确点说应该是计算,是可以被分割的,分割成一小块一小块的计算,而且不互相依赖。抽象示例如下,

class WorkerTask implements Runnable {

    private Data data;
    private SplittedCode code;

    private CountDownLatch latch;

    public WorkerTask(Data data, SplittedCode code, CountDownLatch latch) {
        this.data = data;
        this.code = code;
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            code.run(data);
        } finally {
            latch.countDown();
        }
    }

}
class LeaderTask implements Runnable {

    private Data data;
    private SplittedCode code;

    public LeaderTask(Data data, SplittedCode code) {
        this.data = data;
        this.code = code;
    }

    @Override
    public void run() {
        SplittedCode[] codes = code.split();
        CountDownLatch latch = new CountDownLatch(codes.length);
        for (SplittedCode code : codes) {
            LWThreadPoolProvider.workerPool().submit(
                    new WorkerTask(data, code, latch));
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();  // TODO
        }
    }

}
public class LWExecutor implements Executor {
    @Override
    public void exec(Code code, Data data) {
        if(code instanceof SplittedCode) {
            LWThreadPoolProvider.leaderPool().submit(
                    new LeaderTask(data, (SplittedCode)code));
        }
    }
}

采用分级线程池,将整体计算提交给到LeaderPoolLeaderPool对计算进行拆分,拆成一个个小的计算提交给WorkerPool。两个线程池采用不同的RejectedExecutionHandler

    public static ExecutorService leaderPool() {
        if(LEADER_POOL == null) {
            LEADER_POOL = new ThreadPoolExecutor(
                    getConfig().getLeaderPoolCoreSize(),
                    getConfig().getLeaderPoolMaxSize(),
                    getConfig().getLeaderThreadKeepAliveSeconds(),
                    TimeUnit.SECONDS,
                    new LinkedBlockingDeque<Runnable>(getConfig().getLeaderTaskQueueSize()),
                    new ThreadPoolExecutor.AbortPolicy());
        }
        return LEADER_POOL;
    }

    public static ExecutorService workerPool() {
        if(WORKER_POOL == null) {
            WORKER_POOL = new ThreadPoolExecutor(
                    getConfig().getWorkerPoolCoreSize(),
                    getConfig().getWorkerPoolMaxSize(),
                    getConfig().getWorkerThreadKeepAliveSeconds(),
                    TimeUnit.SECONDS,
                    new LinkedBlockingDeque<Runnable>(getConfig().getWorkerTaskQueueSize()),
                    new ThreadPoolExecutor.CallerRunsPolicy());
        }
        return WORKER_POOL;
    }

数据并发

类似的,数据并发就是拆分数据,

class PagedTask implements Runnable {

    private Code code;
    private PagedData data;

    public PagedTask(int taskIndex, int pageSize, Code code, PagedData data) {
        this.code = code;
        this.data = data.subData(taskIndex*pageSize, (taskIndex+1)*pageSize);
    }

    @Override
    public void run() {
        code.run(data);
    }

}
public class PagedExecutor implements Executor {

    private int taskNum;
    private int pageSize;
    private ExecutorService executor;

    public PagedExecutor(int taskNum, int pageSize, int threads) {
        this.taskNum = taskNum;
        this.pageSize = pageSize;
        executor = Executors.newFixedThreadPool(threads);
    }

    @Override
    public void exec(Code code, Data data) {
        if(data instanceof PagedData) {
            for(int taskIndex = 0; taskIndex < taskNum; taskIndex++) {
                PagedTask task = new PagedTask(taskIndex, pageSize,
                        code, (PagedData)data);
                executor.submit(task);
            }
        }
    }
}

将数据拆分,分配给不同的task,每个task有自己的taskIndex,所有task并发执行。



简单了点,轻拍^_^

时间: 03-15

我所理解的并发#2:两种策略的相关文章

Spring声明式事务配置的两种策略SpringAop和Bean后处理器的代理BeanNameAutoProxyCreator

Spring的事务配置有两种:1编程式事务管理配置:2声明式事务管理配置.下面介绍两种声明式事务的配置,声明式事务相比于编程式事务代码耦合更低,无序书写任何事务管理的先关代码.两种声明式事务配置策略分别是:SpringAop事务管理和Bean后处理器的代理BeanNameAutoProxyCreator管理事务. 1.SpringAop事务管理配置 1.1.配置数据源: <bean id="pycDataSource" class="com.mchange.v2.c3p

两种高性能I/O设计模式(Reactor/Proactor)的比较

综述 这篇文章探讨并比较两种用于TCP服务器的高性能设计模式. 除了介绍现有的解决方案,还提出了一种更具伸缩性,只需要维护一份代码并且跨平台的解决方案(含代码示例),以及其在不同平台上的微调. 此文还比较了java.c#.c++对各自现有以及提到的解决方案的实现性能. 系统I/O 可分为阻塞型, 非阻塞同步型以及非阻塞异步型[1.2]. 阻塞型I/O意味着控制权只到调用操作结束了才会回到调用者手里. 结果调用者被阻塞了, 这段时间了做不了任何其它事情. 更郁闷的是,在等待IO结果的时间里,调用者

struts2+spring的两种整合方式

借助于Spring插件(Struts2-spring-plugin-XXX.jar),我们可以非常简单地完成Spring和Struts2的整合,这种整合包括让Action自动装配Spring容器中的Bean,以及让Spring管理应用中的Action两种方式,不管采用哪种方式,完成Struts2和Spring的整合都是非常简单的,而且差别不大.一旦在Web应用中安装了Spring插件,即可充分利用该插件提供的功能: 1,可以通过Spring来创建所有的Action,Interceptor和Res

Java并发编程-创建线程的两种方式及区别

转载请注明:http://blog.csdn.net/UniKylin/article/details/45016117 1.线程和进程的区别 并行:是多个任务在同一时间同时执行,例如多核计算机同时计算的任务可以理解为并行 并发:从微观上看是多个任务抢占一个CPU从而执行自己的任务,轮流执行任务,但是如果遇到资源冲突的时候并没有从根本提高执行效率.但是提高了CPU的使用效率. 前段时间在GitHub上的一幅图可以很好的阐述上面的概念非常形象 2.Java中创建线程的两种方式 1.第一种方式:直接

SpringMVC两种配置理解----web.xml还有JavaConfig

最近因为在上javaEE的课程一直在学习Spring,在网上找了很多资料,发现大多数都是通过web.xml配置的spring,而通过javaConfig配置的比较少,本人自己尝试了两种配置方法. 开发环境Eclipse+Maven+Tomcat ----------------------------------------------------------- 首先我们要清楚Spring整个工作过程---> 盗用Spring in Action里的这张图片,我们可以看到整个流程:(如果想全面了

JAVA EE 项目经常使用知识 之AJAX技术实现select下拉列表联动的两种使用方法(让你真正理解ajax)

ajax 下拉列表联动的使用方法. ajax的定义: AJAX 是一种用于创建高速动态网页的技术. 通过在后台与server进行少量数据交换,AJAX 能够使网页实现异步更新.这意味着能够在不又一次载入整个网页的情况下,对网页的某部分进行更新. ajax效果的一个样例: 区域为空的时候,维护人情况: 选了一个区域后的情况:(选 舒城县 联带出来的维护人员 小刘) 一.原生态的js实现 XMLHttpRequest 是 AJAX 的基础 XMLHttpRequest 对象 全部现代浏览器均支持 X

19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权.因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去.因此,一般情况下,当队列满时,会让生产者交出对

Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权.因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去.因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态.然后等待消费者消费了商品,然后消费者通知生产者队列有空间了.同样地,当

有关文件读取写入 和两种文件打开模式的理解

文件有两种打开模式:文本模式和二进制模式 当c语言程序对文件操作时,先以某种模式打开文件,建立一个缓存去(读写模式下两个):缓存区中有文件的控制信息,然后用I/O函数操作文件. 对于fread和fwrite函数,不管文件是以什么模式打开的,fread和fwrite只会原样复制原始数据,而其他函数可能会对原始函数进行转化. 之后我们再用应用程序打开文件,取决于应用程序对文件的解释. 先写到这,等以后更加深一步了解了再补充.