Java7线程学习笔记(四)

线程执行器

简介
通常,使用Java来开发一个简单的并发应用程序,会创建一些Runnable对象,然后创建对应的Thread对象来执行他们。但是,如果需要开发一个程序来运行大量的并发任务,这个方法将突显如下的劣势:
必须实现所有与Thread对象管理相关的代码,比如线程的创建、结束以及结果的获取。
需要为每一个对象创建一个Thread对象。如果需要执行大量的任务,这将大大的影响应用程序的处理能力。
计算机的资源需要高效的进行控制和管理,如果创建过多的线程,将会导致系统负荷过重。

Java API提供了一套意在解决这些问题的机制,这套机制称之为执行器框架,围绕着Execute接口和它的子接口ExecutorService,以及实现这两个接口的ThreadPoolExecutor类。这套机制分离了任务的创建和执行,通过使用执行器,仅需要实现Runnable接口的对象,然后将这些对象发送给执行器即可。执行器通过创建所需的线程来负责这些Runnable对象的创建、实例化以及运行。但是执行器功能不限于此。它使用了线程池来提高应用程序的性能,避免了不断的创建和销毁线程而导致系统性能下降。
执行器框架另一个重要的优势是Callable接口,它类似于Runable接口,但是提供了两方面的增强。
这个接口的主方法称为call()可返回结果集。
当发送一个callable对象给执行器,将获得一个实现Future接口的对象,可以使用这个对象来控制callable对象的状态和结果。
创建线程执行器
使用执行器框架的第一步是创建ThreadPoolExecutor对象。可以ThreadPoolExecutor类提供的四个构造器或者使用Executors工厂类来创建ThreadPoolExecutor对象。一旦有了执行器,就可以将Runnable或callable对象发送给它去执行了。
仅当线程的数量是合理的或者线程只会运行很短耳朵时间时,适合采用Executors工厂类的newCachedThreadPool()方法来创建执行器。
executor=(ThreadPoolExecutor)Executors.newCachedThreadPool();
创建固定大小的线程执行器
当使用Executors类的newCachedThreadPool()方法创建基本的ThreadPoolExecutor时,执行器运行过程中将碰到线程数量的问题。如果线程池中没有空闲的线程可用,那么执行器将为接收到的每一个任务创建一个新线程,当发送大量的任务给执行器并且任务需要持续较长的时间时,系统将会超负荷,应用程序也将随之性能不佳。
executor=(ThreadPoolExecutor)Executors.newFixedThreadPool(5);
使用Executors类的newFixedThreadPool()方法来创建执行器,这个方法创建了具有线程最大数量值得执行器,如果发送超过线程数的任务给执行器,剩余的任务将被阻塞直到线程池里有空余的线程来处理他们。
Executors工厂类也提供了newSingleThreadExecutor()方法,这是一个创建固定大小线程执行器的极端场景,它将创建一个只有单个线程的执行器,因此,这个执行器只能在同一时间执行一个任务。
在执行器中执行任务并返回结果
执行器框架的优势之一是可以运行并发任务并返回结果。
Callable:这个接口声明了call()方法。可以在这个方法里实现任务的具体逻辑操作。Callable接口是一个泛型接口,这就意味着必须声明call()方法返回的数据类型。
Future:这个接口声明了一起方法获取由Callable对象产生的结果,并管理他们的状态。
运行多个任务并处理第一个结果
当采用多个并发任务来解决一个问题时,往往只关心这些任务中的第一个结果,比如对一个数组进行排序有很多种算法,可以并发启动所有的算法,但是对于指定的数组,第一个得到排序结果的算法就是最快的排序算法。
ThreadPoolExecutor类的invokeAny()方法接收到一个任务列表,然后运行任务,并返回第一个完成任务并且没有抛出异常任务的执行结果。
运行多个任务并处理所有结果
如果想要等待任务结束,可以使用如下两种方法:
如果任务执行结束,那么Future接口的isDone()方法返回true。
在调用shutdown()方法后,ThreadPoolExecutor类的awaitTermination()方法会见线程休眠,直到所有的任务执行结束。
ThreadPoolExecutor类还提供了一个方法,它允许发送一个任务列表给执行器,并等待列表中所有任务执行完成。
resultList=executor.invokeAll(taskList);
在执行器中周期性的执行任务:
使用ScheduledThreadPoolExecutor类来执行周期性的任务。
ScheduledFuture

package com.bh.recipe8.task;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * This class manage the execution of a ExecutableTaks. Overrides
 * the done() method that is called when the task finish its execution 
 *
 */
public class ResultTask extends FutureTask<String> {

    /**
     * Name of the ResultTask. It's initialized with the name of the
     * ExecutableTask that manages
     */
    private String name;

    /**
     * Constructor of the Class. Override one of the constructor of its parent class 
     * @param callable The task this object manages
     */
    public ResultTask(Callable<String> callable) {
        super(callable);
        this.name=((ExecutableTask)callable).getName();
    }

    /**
     * Method that is called when the task finish.
     */
    @Override
    protected void done() {
        if (isCancelled()) {
            System.out.printf("%s: Has been cancelled\n",name);
        } else {
            System.out.printf("%s: Has finished\n",name);
        }
    }

}
package com.bh.recipe8.core;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import com.bh.recipe8.task.ExecutableTask;
import com.bh.recipe8.task.ResultTask;

/**
 * Main class of the example. Creates five tasks that wait a random period of time.
 * Waits 5 seconds and cancel all the tasks. Then, write the results of that tasks
 * that haven't been cancelled.
 *
 */
public class Main {

    /**
     * Main method of the class.
     * @param args
     */
    public static void main(String[] args) {
        // Create an executor
        ExecutorService executor=(ExecutorService)Executors.newCachedThreadPool();

        //Create five tasks
        ResultTask resultTasks[]=new ResultTask[5];
        for (int i=0; i<5; i++) {
            ExecutableTask executableTask=new ExecutableTask("Task "+i);
            resultTasks[i]=new ResultTask(executableTask);
            executor.submit(resultTasks[i]);
        }

        // Sleep the thread five seconds
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }

        // Cancel all the tasks. In the tasks that have finished before this moment, this
        // cancellation has no effects
        for (int i=0; i<resultTasks.length; i++) {
            resultTasks[i].cancel(true);
        }

        // Write the results of those tasks that haven't been cancelled
        for (int i=0; i<resultTasks.length; i++) {
            try {
                if (!resultTasks[i].isCancelled()){
                    System.out.printf("%s\n",resultTasks[i].get());
                }
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            } 
        }
        // Finish the executor.
        executor.shutdown();

    }

}
package com.bh.recipe8.task;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

/**
 * This class implements the task of this example. It waits a random period of time
 *
 */
public class ExecutableTask implements Callable<String> {

    /**
     * The name of the class
     */
    private String name;

    /**
     * Constructor of the class
     * @param name The name of the class
     */
    public ExecutableTask(String name){
        this.name=name;
    }

    /**
     * Main method of the task. It waits a random period of time and returns a message
     */
    @Override
    public String call() throws Exception {
        try {
            Long duration=(long)(Math.random()*10);
            System.out.printf("%s: Waiting %d seconds for results.\n",this.name,duration);
            TimeUnit.SECONDS.sleep(duration);
        } catch (InterruptedException e) {
        }       
        return "Hello, world. I'm "+name;
    }

    /**
     * This method returns the name of the task
     * @return The name of the task
     */
    public String getName(){
        return name;
    }
}

在执行器中分离任务的启动和结果的处理
CompletionService类:该类有一个方法用来发送任务给执行器,还有一个方法为下一个已经执行结束的任务获取Future对象。
处理在执行器中被拒绝的任务
当我们想结束执行器的执行时,调用shutdown()方法来表示执行器应当结束。但是执行器只有等待正在运行的任务或者等待执行的任务结束后,才能真正的结束。
如果在shutdown()方法与执行器结束之间发送一个任务给执行器,这个任务会被拒绝,因为这个时间段执行器已不再接受任务了。ThreadPoolExecutor类提供了一套机制,当任务拒绝时使用这套机制来处理他们。
实现RejectedExe

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值