Java并发编程(四) 任务的执行

1. 启动线程的最基本方式

   在Java中用Runnable表示一个任务,需要在Runnable接口的run方法中实现任务的具体业务逻辑,然后创建一个Thread并调用start方法启动任务。

   例如执行一个匿名任务:

new Thread(new Runnable() {
                              public void run()
                              {
                                 //....
                              }
                         }
           ).start();

 

2. 使用Executor框架执行线程

  Java中执行任务更灵活的方式是使用Executor框架。

  java.util.concurrent.Executor是一个十分简单的接口,只有一个方法execute,参数为Runnable对象,表示对任务处理器的抽象。

public interface Executor
{
   public abstract void execute(Runnable  task);
}

  Executor还有一个更常用的接口是java.util.concurrent.ExecutorService,ExecutorService扩展了Executor接口的功能,添加了开启关闭任务处理器等功能。java.util.concurrent.Executors类是一个工具类,主要提供了很多创建Executor对象以及ExecutorService对象的工厂方法。

  Executor框架实现了将任务的提交与任务的执行解耦,Executor框架基于生产者—消费者模式,任务的提交程序相当于生产者,而执行任务的线程相当于消费者。

  假设我们要实现一个服务器程序,该服务器程序通过80端口接收请求,然后处理请求:

class  MyServer 
{
   
   private class HandleRequestTask implements Runnable
  {
      HandleRequestTask(Socket client)
      {
         //.....
      }
   
      public void run()
      {
         //.....
      }
  }  
public static void main(String[] args) { ServerSocket server = new ServerSocket(80); Socket client = null; while(true) { client = server.accept(); new Thread(new HandleRequestTask(client)).start(); } } }

  在上述服务器中,我们每收到一个请求,就开启一个线程来处理该请求。上述代码中,任务的提交和执行是紧耦合的,而使用Executor接口就可以改进为如下程序:

public  class MyServer
{
    private static final  Executor executor = Executors.newCachedThreadPool();

    private class HandleRequestTask implements Runnable
    {
       HandleRequestTask(Socket client)
       {
          //.....
       }
   
       public void run()
       {
          //.....
       }
    }

     public static void main(String[] args)
     {
         ServerSocket server = new ServerSocket(80);
         Socket client = null;
         while(true)
         {
            client = server.accept();
            executor.execute(new HandleRequestTask(client));
         }
     }
}  

  上述例子使用Executors类的newCachedThreadPool方法创建了一个线程池,该线程池也是对于每个请求采用一个线程来处理,实现的功能和第一个服务器程序一样,但比第一个服务器程序要灵活得多。我们可以使用不同的ExecutorService对象来执行不同的任务提交策略,例如使用newFixedThreadPool方法创建一个包含固定线程数目的线程池,使用固定数目的线程来处理客户端请求。

3. Callable与Future

  Runnable接口表示任务执行的一个抽象,其中只有一个run方法,并且没有返回值,也不抛出任何异常,这样我们就无法了解任务执行是失败了还是成功了,也无法了解任务执行的进度。

  对于需要返回任务执行结果的情况,Callable接口是一个更好的选择。java.util.concurrent.Callable<V>是一个泛型接口类,只有一个call方法,如果任务执行成功,则返回结果(类型为V),否则抛出一个异常。

public interface Callabel<V>
{
   public abstract V call();
}

  在Executors工具类中还提供了一些static的方法用于将Runnable对象转换成Callable对象。

  java.util.concurrent.Future<V>接口用于表示一个任务执行的生命周期,任务的执行状态包括等待执行、正在执行和执行完成三种状态。Future提供了检查任务执行是否完成,以及获得执行结果的方法。

public interface Future<V>
{
   public abstract boolean cancel(boolean mayInterruptIfRunning);
   public abstract V get();
   public abstract V get(long timeout,TimeUnit unit);
   public abstract boolean isCancelled();
   public abstract boolean isDone();
}

  get方法用于获取任务的执行结果,如果任务已经完成则立即返回结果(执行成功)或者抛出异常ExecutionException(执行失败,任务被取消会抛出CancellationException),如果任务尚未完成,则该方法将阻塞至任务完成;cancel方法试图取消任务的执行;isCancelled方法用于判断任务是否在执行之前被取消;isDone方法用于判断任务是否已经处于执行完成状态(包括执行成功和执行失败)。

  java.util.concurrent.FutureTask类是Future接口的实现类,实现了Future接口中的各个方法,FutureTask类也实现了Runnable接口。

 

 

参考资料 《Java并发编程实战》

posted @ 2015-11-05 20:45  jqc  阅读(664)  评论(0编辑  收藏  举报