java并发编程第五章(1) 创建Fork/Join线程池
来源:互联网 发布:csv数据怎么转换成mat 编辑:程序博客网 时间:2024/06/05 21:58
说明:执行器框架(Executor FrameWork) 将任务的创建和执行进行了分离,通过这个框架,只需要实现Runnable接口的对象和使用Executor对象,
然后将Runnable对象发送给执行器,执行器再负责运行这些任务所需要的线程,包括线程的创建,线程的管理和线程的结束。java 7新特性
包括了ExecutorService接口的另一种实现方式,用来解决特殊问题,Fork/Join框架,有时也称为分解/合并框架。
该框架是用来解决能够通过分治技术将问题拆分成小任务的问题。先检查要解决问题的大学,如果大于一个设定的大学,那就将问题拆分成可以通过
框架来执行的小任务。如果问题的大小比设定的大小要小,就可以直接在任务里解决这个问题,然后根据需要返回任务的结果。没有固定的公式来决定问题的参考大小,从而决定一个任务是需要进行拆分或者不需要拆分,拆分与否仍是依赖于任务本身的特性。可以使用在任务中将要处理的
元素的数目和任务执行所需要的时间来决定参考大小。测试不同的参考大小来决定解决问题的最好方案。该框架基于以下两种操作: 1、分解(Fork)操作:当需要将一个任务拆分成更小的多个任务时,在框架中执行这些任务。
2、合并(Join)操作:当一个主任务等待其创建的多个子任务的完成。Fork/Join框架 与Executor框架的主要区别在于工作者窃取算法
该框架使用Join操作让一个主任务等待它所创建的子任务的完成,执行这个任务的线程称之为工作者线程,工作者线程寻找其他仍未被执行的任务,然后开始执行。
通过这种方式,这些线程在运行时拥有所有的有点,进而提升应用程序的性能。 该框架执行任务的限制有:1.任务只能使用fork()和join()方法操作当做同步机制。如果使用其他同步机制,工作者线程就不能执行其他任务,当然这些任务是在同步操作里时。比如在该框架中
将一个任务休眠,正在执行这个任务的工作者线程在休眠期内不能执行另一个任务。
2.任务不能执行I/O操作,比如文件数据的读取和写入。
3.任务不能抛出非运行时异常,必须在代码中处理掉这些异常。该框架的核心是下面两个类组成的,
1.ForkJoinPool: 这个类实现了ExecutorService接口和工作者窃取算法。它管理工作者线程,并提供任务的状态信息,以及任务的执行信息,
2.ForkJoinTask: 这个类是一个将在ForkJoinPool中执行任务的基类。4.该框架提供了一个任务里执行fork()和join()操作的机制和控制任务状态的方法。通常,为了实现fork/join任务需要实现一个以下两个类之一的子类
1.RecursiveAction: 用于任务没有返回结果的场景。
2.RecursiveTask : 用于任务由返回结果的场景。
实例代码:
/** * * @author fcs * @date 2015-7-22 * 描述:Product类,用来存储产品的名称和价格。 * 说明: */public class Product { private String name; private double price; public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; }}
/** * * @author fcs * @date 2015-7-22 * 描述:生成随机产品列表 * 说明: */public class ProductListGenerator { //生成随机产品列表 public List<Product> generate(int size){ List<Product> ret = new ArrayList<Product>(); for(int i =0;i< size;i++){ Product product = new Product(); product.setName("product "+i); product.setPrice(10); ret.add(product); } return ret; }}
/** * * @author fcs * @date 2015-7-22 * 描述:任务执行类 * 说明: */public class Task extends RecursiveAction { private static final long serialVersionUID = -3177053158845985294L; private List<Product> products; private int first; private int last; private double increment; public Task(List<Product> products, int first, int last, double increment) { this.products = products; this.first = first; this.last = last; this.increment = increment; } //分解任务并交给ForkJoinPool执行。 @Override protected void compute() { if((last - first) < 10){ updatePrices(); }else{ int middle = (last+first) / 2; System.out.printf("Task: pending tasks: %s\n",getQueuedTaskCount());; Task t1 = new Task(products,first,middle+1,increment); Task t2 = new Task(products,middle+1,last,increment); invokeAll(t1,t2); } } //更新产品价格 private void updatePrices(){ for(int i = first;i< last;i++) { Product product = products.get(i); product.setPrice(product.getPrice()*(1+increment)); } }}
public class Main { public static void main(String[] args) { ProductListGenerator generator = new ProductListGenerator(); List<Product> productList = generator.generate(10000); Task task = new Task(productList,0, productList.size(),0.20); ForkJoinPool pool = new ForkJoinPool(); pool.execute(task); do{ System.out.printf("Main: Thread Count: %d\n",pool.getActiveThreadCount()); System.out.printf("Main: Thread Steal : %d\n",pool.getStealCount()); System.out.printf("Main: Parallelism: %d\n",pool.getParallelism()); try { TimeUnit.MILLISECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } }while(!task.isDone()); if(task.isCompletedNormally()){ System.out.printf("Main:The process has completed normally.\n"); } for(int i =0 ;i< productList.size();i++){ Product product = productList.get(i); if(product.getPrice() != 12){ System.out.printf("Product %s: %f\n",product.getName(),product.getPrice()); } } System.out.println("Main End of the program."); pool.shutdown(); }}
总结:
1、 创建Fork/Join线程池
1.实例使用同步的方式执行任务,当一个主任务执行两个或者更多的子任务时,这个主任务将等待子任务的完成,用这种方式,执行主任务的称为工作者线程。
它将寻找其他的子任务来执行,并在子任务执行的时间里利用所有线程的优势。
2.如果将要实现的任务没有返回任何结果,将采用RecursiveAction类作为实现任务的基类。
说明:ForkJoinPool()类提供了以下方法用于执行任务
1.execute(RunnableTask):是本例的另一个重载方法。这个方法发送一个Runnable任务给ForkJoinPool类。
注意:使用Runnable对象时ForkJoin类就不采用工作者窃取算法,ForkJoinPool类仅在使用ForkJoinTask类时才采用该算法。
2.invoke(ForkJoinTask task): **ForkJoinPool类的execute()方法是异步调用的,而ForkJoinPool类的方法invoke()是同步调用的。
这个方法直到传递进来的任务执行结束后才会返回。**
3.也可以使用ExecutorService类中的invokeAll()和invokeAny()方法。
接收Callable对象作为参数,使用Callable对象时,ForkJoinPool类就不采用工作者窃取算法。
因此推荐使用执行器框架执行使用Callable对象的任务。
总结:虽然ForkJoinPool类是设计用来执行ForkJoinTask对象的,但是也可以直接用来执行Runnable和Callable对象。
也可以使用adapt()方法来接收一个Callable对象或者Runnable对象。然后将之转换为一个ForkJoinTask对象,然后再去执行。
- java并发编程第五章(1) 创建Fork/Join线程池
- Java并发编程-24-创建Fork/Join线程池
- 并发编程--创建Fork/Join线程池
- java并发编程(十八)----(线程池)java线程池框架Fork-Join
- Java并发(十)线程池&fork/join框架
- 【Java并发编程】Fork/Join
- 创建Fork/Join线程池
- Fork and Join(Java并发编程的思路)
- Java 并发编程(七)Fork/Join框架
- Java并发编程--Fork/Join框架使用
- Java 并发编程 Fork Join 一
- Fork/Join并发编程
- Java7并发编程指南——第五章:Fork Join框架
- java并发Fork/join框架-java并发编程的艺术
- 乱弹java并发(九)-- fork/join
- Java并发编程之线程管理(线程创建1)
- Java并发编程核心方法与框架-Fork-Join分治编程(一)
- Java并发编程核心方法与框架-Fork-Join分治编程(二)
- sap上线前的十大思考
- 暑假学习随笔
- iOS 开发之IPad的设计与实现
- 解决VM虚拟机连不上网络的问题
- sap打油诗
- java并发编程第五章(1) 创建Fork/Join线程池
- C语言函数指针与回调函数
- ios开发之猜数字游戏
- mapreduce处理数据倾斜的一些方法
- CRC32
- UVa 1588 kickdown
- spring mvc +extjs 学习
- 根据已知入栈顺序判断一个数组是否是出栈顺序
- NSArray