一.线程并发控制  Thread、Executor、ForkJoin和Actor
  1.裸线程
      Runnable接口,调用.start()开始,没有现成的API来结束线程,你需要自己来实现,通过类似boolean类型的标记来通讯

使用Runnable对象,然后创建对应的Thread对象来控制程序中这些线程的创建、执行以及线程的状态

2.ExecutorFramework(Executor和ExecutorService接口):执行器框架将任务的创建和执行进行了分离,通过这个框架只需要实现Runnable接口的对象和使用Executor对象,然后将Runnable对象发送给执行器,执行器再负责这些任务锁需要的线程,包括线程的创建管理已经线程的结束

  public interface Executor {
    void execute(Runnable command);
  }
  ExecutorService管理executor的生命周期,以及CompletionService会抽象掉更多细节,作为已完成任务的队列
  Executors.newFixedThreadPool(4)
   3.通过并行流,使用ForkJoinPool (FJP)

Fork/Join 分解/合并框架 

用来解决分治技术,将问题拆分成若干小任务,在一个任务中,先检查要解决任务的问题的大小,决定是否再拆分任务。如果不需要拆分任务了,就在当前任务中解决问题。然后根据需要返回任务的结果。

ForkJoinPool类看作一个特殊的Executor执行器类型,这个框架包括以下两个操作:
 分解(Fork)操作:当需要将一个任务拆分成更小的多个任务时,在框架中执行这些任务
 合并(Join)操作: 当一个主任务等待其创建的多个子任务的完成执行。
Fork/Join 和 ExecutorFramework 主要的区别在于 工作窃取算法(Work-StealingAlgroithms),使用Join操作让一个主任务等待它所创建的子任务的完成,执行这个任务的线程称之为 工作者线程(worker Thread),工作者线程会寻找其他任未被执行的任务,然后开始执行。 什么是工作窃取算法?:就是指某个线程从其他队列里窃取任务来执行。从而提升了性能。

为了达到以上的效果,Fork/Join框架有以下几点限制:
a. 任务只能使用fork() 和 join() 操作当同步机制。如果使用其他的同步机制,工作线程就不能执行其他的任务,当然这些任务是在同步操作里时。比如在Fork/Join框架中将一个任务休眠,正在执行这个任务的工作者线程在休眠期内不能执行另一个任务。
b. 任务不能执行I/O操作,比如文件数据的读取与写入
c. 任务不能抛出非运行时异常(Checke Exception),必须在代码中处理这些异常

4.actor模型中  Akka Actors
      actor模型中,一切都看做是一个actor。一个actor是一个计算实体,它可以从其他actor那里接收消息。在应答消息时,它可以给其他actor发送消息,或者创建新的actor并与之交互,或者只改变自己的内部状态。
      这是一个非常强大的概念。生命周期和消息传递由你的框架来管理,你只需要指定计算单元是什么就可以了。另外,actor模型强调避免全局状态,这会带来很多便利。你可以应用监督策略,例如免费重试,更简单的分布式系统设计,错误容忍度等等。

二.简单应用举例

转自  http://blog.csdn.net/mr_zhuqiang/article/details/48300229

Fork/Join框架的核心是由ForkJoinPool和ForkJoinTask组成

  ForkJoinPool : 这个类实现了 ExecutorServic接口和工作窃取算法。它管理工作者线程,并提供任务的状态信息,以及任务的执行信息
  ForkJoinTask :是一个将在ForkJoinPool中执行的任务的基类
  Fork/Join框架提供了在一个任务里执行fork和join操作的机制和控制任务状态的方法,通常,为了实现Fork/Join任务,需要实现以下两个类之一的子类。

    RecursiveAction :用于任务没有返回结果的场景,一个ForkJoinTask任务类,递归无结果的任务类。类似callable一样的线程任务,

    RecursiveTask :  用于任务有返回结果的场景

下示例描述了,批量修改很多商品的价格,使用Fork/Join线程池 和 RecursiveAction(ForkJoinTask)来实现 递归的分配任务执行

    public static void main(String[] args) throws InterruptedException {
// 生成商品数据
List<Product> list = new ArrayList<Product>();
for (int i = 0; i < 40; i++) {
Product p = new Product("苹果" + i, 10);
list.add(p);
}
///////////////////////////////////////////////
ForkJoinPool fjp = new ForkJoinPool(); Task task = new Task(list, 0, list.size(), 19);
fjp.execute(task);
// fjp.shutdown(); //关闭线程池
// fjp.awaitTermination(1, TimeUnit.MINUTES);
//等待超时。结合shutdown来让任务一完成就继续执行下面的代码 // 使用循环的方式来查看任务的信息
do {
System.out.printf("活跃线程:%s,这一个参数 %s,并行执行的最大数量:%s\n",
fjp.getActiveThreadCount(),
fjp.getStealCount(),
fjp.getParallelism()); } while (!task.isDone()); // 如果任务还未完成,则继续获取信息
// 如果这个任务完成没有抛出异常并没有取消。
if (task.isCompletedNormally()) {
System.out.println("main:任务完成");
} System.out.println("main:------------------------------ 打印任务结果");
for (Product product : list) {
int price = product.getPrice();
String name = product.getName();
if (price != 19) { // 结果不是所期望的。就打印出来
System.out.println(name + "," + price);
}
}
System.out.println("main:------------------------------ 打印任务结束");
}
} class Product {
private String name;
private int price; public Product(String name, int price) {
this.name = name;
this.price = price;
} public String getName() {
return name;
}
......
} class Task extends RecursiveAction {
private List<Product> list; // 所有任务
private int start; // 处理任务的开始索引
private int end; // 处理任务的结束索引
private int price; // 更改的价格 public Task(List<Product> list, int start, int end, int price) {
this.list = list;
this.start = start;
this.end = end;
this.price = price;
} @Override
protected void compute() {
if (end - start <= 10) { // 每个task 只能处理10条数据。
System.out.printf("起始:start:%s,end:%s\n", start, end);
update();
} else { // 多余的数据,则需要分给更多的任务
int middle = (end + start) / 2; // 因为是索引。所以需要开始和结尾相加,然后除以2 就能得到
// 两个索引之间的数值
Task task1 = new Task(list, start, middle, 19);
Task task2 = new Task(list, middle, end, 19); System.out.printf("分析:middle:%s,start:%s,end:%s\n", middle, start, end); // 方便推算
// 这里把任务分成了2半递归执行
invokeAll(task1, task2);
}
} // 根据给定的起始索引和结束索引更新结果
private void update() {
for (int i = start; i < end; i++) {
Product product = list.get(i);
product.setPrice(price); System.out.printf("%s,修改了价格,索引:%s,%s,%s\n",
Thread.currentThread().getName(),
i,product.getName(),product.getPrice() );
}
}
}

结果分析:
   起始信息:我们有40个商品,每个任务处理10个商品。刚好4个工作线程处理。
  分析信息:去中间索引,这个分析在商品数量不能被2整除的时候很有用,在不能被2整除的情况下,该示例任然会尽可能的均衡分配任务的数量
工作原理
  invokeAll方法来执行一个主任务锁创建的多个子任务,这个是一个同步的调用,主任务将等待子任务的完成,然后继续执行(有可能是结束),当这个主任务等待它的子任务时,执行这个主任务的工作者线程接收另一个等待执行的任务并开始执行(并行),正因为有了这个行为,所以说Fork/Join框架提供了一种比Runnable和Callable对象更加高效的任务管理机制。
  ForkJoinTask类的invokeAll方法是执行器框架ExecutorFramework和Fork/Join框架之间的主要差异之一。在执行器框架中。
  在执行器框架中:所有的任务必须发送给执行器
  在Fork/Join框架:线程池中包含了待执行方法的任务,任务的控制也是在线程池中进行的,我们在task类中使用了invokeAll方法,task类继承了RecursiveAction,而RecursiveAction类则继承了ForkJoinTask.

.常用方法

1.fork join get

fork()方法允许ForkJoinTask任务异步执行,也允许一个新的ForkJoinTask从存在的ForkJoinTask中被启动。
   join()方法允许一个ForkJoinTask等待另一个ForkJoinTask执行完成。

fork()只会让ForkJoinPool调度一个新的任务,而不会创建子虚拟机。

RecursiveTask.join() : 也是用来获取任务的合并结果

RecursiveTask.get(long timeout,TimeUnit unit) : 该方法,是给定一个指定的超时时间,如果超时还没有返回结果则返回null

invokeAll(task1,task2): 是一个同步的方法,任务会被挂起,等待子任务发送到线程池中并且直到完成

2.RecursiveAction 和RecursiveTask

RecursiveAction的实例代表执行没有返回结果。
   RecursiveTask会有返回值。下面例子 返回值

public class ForkJoin2Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//生成随机矩阵
final int rows = 10000; // 矩阵行数
final int cols = 10000; // 矩阵列数
final int number = 5; // 查找的数字
long start = System.currentTimeMillis();
MatrixMock mock = new MatrixMock(rows, cols, number); // 生成矩阵对象
long end = System.currentTimeMillis();
System.out.println("创建矩阵花费时间:" + (end - start)); //执行任务
ForkJoinPool pool = new ForkJoinPool();
Task2 task = new Task2(mock, 0, rows, 5); start = System.currentTimeMillis();
pool.execute(task);
pool.shutdown();
//
pool.awaitTermination(1, TimeUnit.MILLISECONDS);
System.out.println("线程搜索的结果是:" + task.get()); end = System.currentTimeMillis();
System.out.println("线程搜索时间是:" + (end - start)); start = System.currentTimeMillis();
int temp = 0;
for (int i = 0; i < rows; i++) {
int[] rs = mock.getRow(i);
for (int row : rs) {
if (5 == row) {
temp++;
}
}
}
end = System.currentTimeMillis();
System.out.println("单线程搜索结果是:" + temp);
System.out.println("单线程搜索时间是:" + (end - start));
}
} // 任务类。查找数字出现的次数
class Task2 extends RecursiveTask<Integer> {
private static final long serialVersionUID = 1L;
private MatrixMock mock;
private int start; // 查询起始行索引
private int end; // 查询结束行索引
private int num; // 要查找的数字 public Task2(MatrixMock mock, int start, int end, int num) {
this.mock = mock;
this.start = start;
this.end = end;
this.num = num;
} @Override
protected Integer compute() {
int result = 0; if (end - start < 100) { // 每个任务最多负责5行数据
result = this.search();
// 适合矩阵小的时候 查看对比结果
// System.out.printf("%s,搜索起始行是:%s-%s,搜索结果是:%s\n",Thread.currentThread().getName(),start,end,result);
} else { // 否则则拆分成两个子任务
int mid = (end + start) / 2;
Task2 task1 = new Task2(mock, start, mid, num);
Task2 task2 = new Task2(mock, mid, end, num);
invokeAll(task1, task2);
try {
result = task1.get() + task2.get(); // 两个结果相加,要想到 该框架的特性就是 递归
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
return result;
} // 计算当前任务分配的行数
private int search() {
int result = 0;
for (int i = start; i < end; i++) {
int[] rows = mock.getRow(i);
for (int row : rows) {
if (num == row) {
result++;
}
}
}
return result;
}
} // 随机矩阵
class MatrixMock {
private int[][] data; public MatrixMock(int size, int cols, int number) {
data = new int[size][cols];
Random random = new Random(); int counter = 0;
// 用随机数为矩阵赋值。每生成一个字,就用它跟要查找的数字比较,进行比较。如果一致,就用计数器加1
for (int i = 0; i < size; i++) {
for (int j = 0; j < cols; j++) {
data[i][j] = random.nextInt(10);
if (data[i][j] == number) {
counter++;
}
}
}
// 用来验证多线程查找的正确性
System.out.printf("在矩阵中找到了数字:%d,%d次\n", number, counter);
// 测试的时候,可以放开此代码,能打印出 矩阵分布图。当然需要矩阵10 * 10 比较小的收,控制台才能装得下
// for (int i = 0; i < data.length; i++) {
// for (int j = 0; j < data[i].length; j++) {
// System.out.printf(data[i][j] + " | ");
// }
// System.out.println("");
// }
} public int[] getRow(int row) {
if (row >= 0 && row < data.length) {
return data[row];
}
return null;
}
}

thread_fork/join并发框架1的更多相关文章

  1. thread_fork/join并发框架2

    转自  http://blog.csdn.net/mr_zhuqiang/article/details/48300229 三.使用异步方式 invokeAll(task1,task2); 是同步方式 ...

  2. Java 7 Fork/Join 并行计算框架概览

    应用程序并行计算遇到的问题 当硬件处理能力不能按摩尔定律垂直发展的时候,选择了水平发展.多核处理器已广泛应用,未来处理器的核心数将进一步发布,甚至达到上百上千的数量.而现在 很多的应用程序在运行在多核 ...

  3. Java 并发系列之十:java 并发框架(2个)

    1. Fork/Join框架 2. Executor框架 3. ThreadPoolExecutor 4. ScheduledThreadPoolExecutor 5. FutureTask 6. t ...

  4. 深入理解Java并发框架AQS系列(一):线程

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.概述 1.1.前言 重剑无锋,大巧不工 读j.u.c包下的源码,永远无法绕开的经典 ...

  5. 深入理解Java并发框架AQS系列(三):独占锁(Exclusive Lock)

    一.前言 优秀的源码就在那里 经过了前面两章的铺垫,终于要切入正题了,本章也是整个AQS的核心之一 从本章开始,我们要精读AQS源码,在欣赏它的同时也要学会质疑它.当然本文不会带着大家逐行过源码(会有 ...

  6. 协程并发框架gevent及其用法

    gevent是python的一个并发框架,采用协程实现并发目的,用起来也非常简单 gevent的docs:http://www.gevent.org/contents.html 一个最简单的例子: i ...

  7. Python 开源异步并发框架的未来

    http://segmentfault.com/a/1190000000471602 开源 Python 是开源的,介绍的这几个框架 Twisted.Tornado.Gevent 和 tulip 也都 ...

  8. Python开源异步并发框架

    Python开源异步并发框架的未来 2014年3月30日,由全球最大的中文IT社区CSDN主办的“开源技术大会·” (Open Source Technology Conference ,简称OSTC ...

  9. J.U.C并发框架

    转载:http://itindex.net/detail/48869-j.u.c-%E6%A1%86%E6%9E%B6 J.U.C并发框架 作者:Doug Lea SUNY Oswego Oswego ...

随机推荐

  1. nginx做反向代理负载均衡 Java怎么获取后端服务器获取用户IP

    nginx做反向负载均衡,后端服务器获取真实客户端ip   首先,在前端nginx上需要做如下配置: location / proxy_set_hearder host                 ...

  2. nodejs express 框架解密3-中间件模块

    本文档是基于express 3.4.6 的 在上篇中我们提到了中间件,这篇主要解释这个模块,middleware.js 为: var utils = require('./utils'); /** * ...

  3. 【LeetCode】257. Binary Tree Paths

    Binary Tree Paths Given a binary tree, return all root-to-leaf paths. For example, given the followi ...

  4. leveldb - log格式

    log文件在LevelDb中的主要作用是系统故障恢复时,能够保证不会丢失数据.因为在将记录写入内存的Memtable之前,会先写入Log文件,这样即使系统发生故障,Memtable中的数据没有来得及D ...

  5. 【转】ASP.NET WEB API系列教程

    from: 西瓜小强 http://www.cnblogs.com/risk/category/406988.html ASP.NET Web API教程(六) 安全与身份认证 摘要: 在实际的项目应 ...

  6. JPA与Hibernate的关系

    1.JPA JPA全称: Java Persistence API  JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中.  JPA的出现?  JPA ...

  7. 【cocos2d-x 手游研发----目录】

    感谢大家一直支持我写这样一系列的博客,从中我自己也获益良多,cocos2d-x这样一款非常棒的引擎,是值得我们去学习和分享的,谈到分享,那我就把这套写了差不多一两个月的框架给大家开源下载,写的很一般, ...

  8. 如何安装最新版本的memcached

    转载自孟叔的博客:  https://learndevops.cn/index.php/2016/06/10/how-to-install-the-latest-version-of-memcache ...

  9. ubuntu下 mysql5.6.4 +sphinx安装

    安装mysql 5.6.4 下载源码 安装cmake sudo apt-get install cmake 进入mysql源码包: 创建mysql用户与用户组 groupadd mysql usera ...

  10. Vim安装jedi-vim提示的一个错误

    (仅为了提醒自己) 第一次的安装方法好像是通过 bundle安装的,好像是通过这个安装的并不是最新的版本,然后删除了通过下面的方法,最重要的是要执行 git submodule update --in ...