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对象,然后再去执行。

0 0