java并发编程第五章(1) 创建Fork/Join线程池

说明:执行器框架(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对象,然后再去执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值