zoukankan      html  css  js  c++  java
  • 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并发编程实战》

  • 相关阅读:
    Add Binary
    Java笔记之String
    Java笔记之数组
    Merge Two Sorted Lists
    Remove Nth Node From End of List
    Longest Common Prefix
    Roman to Integer
    Palindrome Number
    Reverse Integer
    _cdel stdcall
  • 原文地址:https://www.cnblogs.com/jqctop1/p/4923261.html
Copyright © 2011-2022 走看看