JAVA多线程的控制

 在Java 5.0之前启动一个任务是通过调用Thread类的start()方法来实现的,任务的提于交和执行是同时进行的,如果你想对任务的执行进行调度或是控制 同时执行的线程数量就需要额外编写代码来完成。5.0里提供了一个新的任务执行架构使你可以轻松地调度和控制任务的执行,并且可以建立一个类似数据库连接 池的线程池来执行任务。这个架构主要有三个接口和其相应的具体类组成。这三个接口是Executor, ExecutorService、ScheduledExecutorService,让我们先用一个图来显示它们的关系:


图的左侧是接口,图的右侧是这些接口的具体类。注意Executor是没有直接具体实现的。

Executor 接口: 是用来执行Runnable任务的,它只定义一个方法:

  • execute(Runnable command ):执行Ruannable类型的任务

ExecutorService 接口: ExecutorService继承了Executor的方法,并提供了执行Callable任务和中止任务执行的服务,其定义的方法主要有:

  • submit(task ):可用来提交Callable或Runnable任务,并返回代表此任务的Future对象
  • invokeAll(collection of tasks ):批处理任务集合,并返回一个代表这些任务的Future对象集合
  • shutdown():在完成已提交的任务后关闭服务,不再接受新任务
  • shutdownNow():停止所有正在执行的任务并关闭服务。
  • isTerminated():测试是否所有任务都执行完毕了。
  • isShutdown():测试是否该ExecutorService已被关闭

ScheduledExecutorService 接口 在ExecutorService的基础上,ScheduledExecutorService提供了按时间安排执行任务的功能,它提供的方法主要有:

  • schedule(task, initDelay ): 安排所提交的Callable或Runnable任务在initDelay指定的时间后执行。
  • scheduleAtFixedRate():安排所提交的Runnable任务按指定的间隔重复执行
  • scheduleWithFixedDelay():安排所提交的Runnable任务在每次执行完后,等待delay所指定的时间后重复执行。

 

重要的Executors

虽然以上提到的接口有其实现的具体类,但为了方便Java 5.0建议使用Executors的工具类来得到Executor接口的具体对象,需要注意的是Executors是一个类,不是Executor的复数 形式。Executors提供了以下一些static的方法:

  • callable(Runnable task): 将Runnable的任务转化成Callable的任务
    • newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
    • newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
    • newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
    • newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
    • newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

 

举例说明:

应用Executors来建立Thread pool

 

有时候您需要建立一堆Thread来执行一些小任务,然而频繁的建立Thread有时会是个开销,因为Thread的建立必须与作业系统互动,如果能建立一个Thread pool来管理这些小的Thread并加以重复使用,对于系统效能会是个改善的方式。

您可以使用Executors来建立Thread pool,Executors有几个static方法,列出如下:

方法 说明 newCachedThreadPool 建立可以快取的Thread,每个Thread预设可idle 60秒

newFixedThreadPool

包括固定数量的Thread

newSingleThreadExecutor

只有一个Thread,循序的执行指定给它的每个任务 newScheduledThreadPool 可排程的Thread newSingleThreadScheduledExecutor 单一可排程的Thread

 

举个简单的实例,下面的程式使用newFixedThreadPool方法建立Thread pool,当中包括五个可以重复使用的Thread,您可以指定Runnable物件给它,程式中会产生十个Runnable物件,由于Thread pool中只有五个可用的Thread,所以后来建立的五个Runnable必须等待有空闲的Thread才会被执行:

  • ExecutorDemo.java

package onlyfun.caterpillar;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class ExecutorDemo {

    public static void main(String[] args) {

       ExecutorService service = Executors.newFixedThreadPool(5);

       for(int i = 0; i < 10; i++) {

         final int count = i;

        service.submit(new Runnable() {

                               public void run() {

                              System.out.println(count);

                               try {

                               Thread.sleep(2000);

                                    } catch (InterruptedException e) {

                                             e.printStackTrace();

                                                         }

                                                                     }

                                                                               });

                                        }

                     service.shutdown(); // 最后记得关闭Thread pool }}submit()方法也接受实作Callable介面的物件,最后传回Future物件,可以取得Callable执行过后的传回结果。

如果想利用Executors进行排程,例如排定某个工作30秒后执行: ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor( );
                    scheduler.schedule(new Runnable( ) {
                               public void run() {
                                   // 排程工作
                               }
                           },
                           30, TimeUnit.SECONDS);或排定某个工作5秒后执行,之后每30秒执行一次: ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor( );
        final ScheduledFuture future = scheduler.scheduleAtFixedRate(new Runnable( ) {
                               public void run() {
                                   // 排程工作
                                   System.out.println("t");
                               }
                           },
                           0, 5, TimeUnit.SECONDS);
      
        // 排定 60 秒后取消future
        scheduler.schedule(new Runnable( ) {
            public void run( ) {
              future.cancel(false);
            }
          }, 60, TimeUnit.SECONDS);
如上所示,想要取消排程任务,可以呼叫ScheduledFuture的cancel()方法。

posted @ 2012-11-30 11:14  学无先后 达者为先  阅读(546)  评论(0)    收藏  举报