首页 > 编程语言 > 详细

线程执行器

时间:2015-10-25 10:47:46      阅读:323      评论:0      收藏:0      [点我收藏+]

  自从Java5开始,Java并发API提供了一套意在解决这些问题的机制。这套机制称之为执行器框架(Executor Framework),围绕着Executor接口和它的子接口ExecutorService,以及实现这两个接口的ThreadPoolEexecutor类展开。这套机制分离了任务的创建和执行。通过使用执行器,仅需要实现Runnable接口的对象,然后将这些对象发送给执行器即可。执行器通过创建所需的线程,来负责这些Runnable对象的创建、实例化以及运行。但是执行器功能不限于此,它使用了线程池来提高应用程序的性能。当发送一个任务给执行器时,执行器会尝试使用线程池中的线程来执行这个任务,避免了不断的创建和销毁线程而导致系统性能下降。

  执行器框架的另一个重要的优势是Callable接口。它类似于Runnable接口,但是却提供了两个方面的增强。

  • 这个接口的主方法名称为call(),可以返回结果。
  • 当发送一个Callable对象给执行器时,将获得一个实现了Future接口的对象。可以使用这个对象来控制Callable对象的状态和结果。

1. 创建线程执行器

  使用执行器框架的第一步是创建ThreadPoolExecutor对象。可以ThreadPoolExecutor类提供的四个构造器或者使用Executors工厂类来创建ThreadPoolExecutor对象。一旦有了执行器,就可以将Runnable或Callable对象发送给它去执行了。

  下面我们将学习如何使用两种操作来实现一个范例,这个范例将模拟一个Web服务器来应对来自不同客户端的请求。

1. 实现Web服务器执行的任务,创建一个名为Task的类,并实现Runnable接口。

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class Task implements Runnable {
    private Date initDate;
    private String name;
    public Task(String name){
        initDate = new Date();
        this.name = name;
    }
    @Override
    public void run() {
        System.out.printf("%s:Task %s: Created on: %s\n", Thread.currentThread().getName(), name, initDate);
        System.out.printf("%s:Task %s: Started on: %s\n", Thread.currentThread().getName(), name, new Date());
        try {
            Long duration = (long) (Math.random()*10);
            System.out.printf("%s:Task %s: Doing a task during %d seconds\n", Thread.currentThread().getName(), name, duration);
            TimeUnit.SECONDS.sleep(duration);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.printf("%s:Task %s: Finished on: %s\n", Thread.currentThread().getName(), name, new Date());
    }

}

2. 创建一个名为Server的类,它将执行通过接收器收到的每一个任务。

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class Server {
    private ThreadPoolExecutor executor;
    
    public Server(){
        //通过工厂类创建
        executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();;
    }
    
    public void executTask(Task task){
        System.out.println("Server: A new task has arrived\n");
        executor.execute(task);
        System.out.printf("Server: Pool Size: %d\n", executor.getPoolSize());
        System.out.printf("Server: Active Count: %d\n", executor.getActiveCount());
        System.out.printf("Server: Completed Tasks: %d\n", executor.getCompletedTaskCount());
    }
    
    public void endServer(){
        executor.shutdown();
    }
}

3. 实现范例的主类,创建Main类,并实现main()方法。

public class Main {

    public static void main(String[] args) {
        Server server = new Server();
        for(int i=0;i<100;i++){
            Task task = new Task("Task"+i);
            server.executTask(task);
        }
        server.endServer();
    }
}

  如果要执行新任务,缓存线程池就会创建新线程;如果线程所运行的任务执行完成后并且这个线程可用,那么缓存线程池将重用这些线程。线程重用的优点是减少了创建新线程所花费的开销。然而,新任务固定会依赖线程执行,因此缓存池也有缺点,如果发送过多的任务给执行器,系统的负荷将会过载。

  备注:仅当线程的数量是合理的或者线程只会运行很短的时间时,适合使用Executors工厂类的newCachedThreadPool()方法来创建执行器。

  执行器以及ThreadPoolExecutor类的一个重要的特性是,通常需要显式地去结束它。如果不这样做,那么执行器将继续执行,程序不会结束。为了完成执行器的执行,可以使用ThreadPoolExecutor类的shutdown()方法。

2. 创建固定大小的线程执行器

  当使用Executors类的newCachedThreadPool()方法创建基本的ThreadPoolExecutor时,执行器运行过程中将碰到线程数量的问题。如果线程池里没有空闲的线程可用,那么执行器将为接收到的每一个任务创建一个新的线程,当发送大量的任务给执行器并且任务需要持续较长的时间时,系统就会超负荷,应用程序也将随之性能不佳。

  为了避免这个问题,Executors工厂类提供了一个方法来创建一个固定大小的线程执行器。这个执行器有一个线程数的最大值,如果发送超过这个最大值的任务给执行器,执行器将不再创建额外的线程,剩下的任务将被阻塞直到执行器有空闲的线程可用。这个特性可以保证执行器不会给应用程序带来性能不佳的问题。

  将上面的范例进行修改如下:

1. 将Server类中的执行器构造器进行修改,使用newFixedThreadPool()方法来创建执行器,并增加一条日志信息。

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class Server {
    private ThreadPoolExecutor executor;
    
    public Server(){
        //通过工厂类创建
        executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);;
    }
    
    public void executTask(Task task){
        System.out.println("Server: A new task has arrived\n");
        executor.execute(task);
        System.out.printf("Server: Pool Size: %d\n", executor.getPoolSize());
        System.out.printf("Server: Active Count: %d\n", executor.getActiveCount());
        System.out.printf("Server: Completed Tasks: %d\n", executor.getCompletedTaskCount());
        System.out.printf("Server: Task Count: %d\n", executor.getTaskCount());
    }
    
    public void endServer(){
        executor.shutdown();
    }
}

  getTaskCount()方法可以用来显示有多少个任务已经发送给执行器。

  Executors工厂类也提供了newSingleThreadExecutor()方法。这是一个创建固定大小线程执行器的极端场景,它将创建一个只有单个线程的执行器。因此,这个执行器只能在同一时间执行一个任务。

 

线程执行器

原文:http://www.cnblogs.com/gaopeng527/p/4908295.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!