• Tags
  •         
  • www.breakyizhan.com
  •    

    先决条件:线程多线程

    Java中的Callable

    有两种创建线程的方法 - 一种是通过扩展Thread类,另一种是通过创建一个带有Runnable的线程。但是,Runnable中缺少的一个特性是,当终止时,即run()完成时,我们无法生成线程返回结果。为了支持此功能,Callable接口存在于Java中。

    Callable vs Runnable

    • 为了实现Runnable,需要实现run()方法,该方法不返回任何内容,而对于Callable,需要实现call()方法,该方法在完成时返回结果。请注意,无法使用Callable创建线程,只能使用Runnable创建线程。
    • 另一个区别是call()方法可以抛出异常,而run()则不能。

    必须重写以实现Callable的方法签名。

    public Object call() throws Exception;

    以下是Callable示例的代码,它将在大约0 - 4秒的延迟后返回一个随机数。

    // Java program to illustrate Callable
    // to return a random number
    import java.util.Random;
    import java.util.concurrent.Callable;
    import java.util.concurrent.FutureTask;
    
    class CallableExample implements Callable
    {
    
        public Object call() throws Exception
        {
            // Create random number generator
            Random generator = new Random();
    
            Integer randomNumber = generator.nextInt(5);
    
            // To simulate a heavy computation,
            // we delay the thread for some random time
            Thread.sleep(randomNumber * 1000);
    
            return randomNumber;
        }
    }
    

    Java中的Future

    当call()方法完成时,answer必须存储在主线程已知的对象中,以便主线程可以知道线程返回的结果。程序将如何存储并在以后获得此结果?为此,可以使用Future对象。将Future视为一个拥有结果的对象 - 它现在可能无法保持它,但它将来会这样做(一旦Callable返回)。因此,Future基本上是主线程可以跟踪其他线程的进度和结果的一种方式。要实现此接口,必须重写5个方法,但如下例所示,使用库中的具体实现,此处仅列出了重要的方法。

    观察Callable和Future做两件事 - Callable类似于Runnable,因为它封装了一个意图在另一个线程上运行的任务,而Future用于存储从不同线程获得的结果。事实上,Future也可以与Runnable一起使用,当执行者进入画面时,这将变得清晰。

    • public boolean cancel(boolean mayInterrupt):用于停止任务。如果任务尚未启动,它将停止该任务。如果已启动,则仅当mayInterrupt为true时才会中断任务。
    • public Object get()throws InterruptedException,ExecutionException:用于获取任务的结果。如果任务完成,则立即返回结果,否则等待任务完成,然后返回结果。
    • public boolean isDone():如果任务完成则返回true,否则返回false

    要创建线程,需要Runnable。要获得结果,需要Future。

    Java库具有FutureTask的具体类型,它实现了Runnable和Future,方便地结合了两个功能。
    可以通过为其构造函数提供Callable来创建FutureTask。然后将FutureTask对象提供给Thread的构造函数以创建Thread对象。因此,间接地,使用Callable创建线程。为了进一步强调,请注意,无法使用Callable直接创建线程。

    以下是使用Callable和FutureTask的完整示例的代码。

    // Java program to illustrate Callable and FutureTask
    // for random number generation
    import java.util.Random;
    import java.util.concurrent.Callable;
    import java.util.concurrent.FutureTask;
    
    class CallableExample implements Callable
    {
    
      public Object call() throws Exception
      {
        Random generator = new Random();
        Integer randomNumber = generator.nextInt(5);
    
        Thread.sleep(randomNumber * 1000);
    
        return randomNumber;
      }
    
    }
    
    public class CallableFutureTest
    {
      public static void main(String[] args) throws Exception
      {
    
        // FutureTask is a concrete class that
        // implements both Runnable and Future
        FutureTask[] randomNumberTasks = new FutureTask[5];
    
        for (int i = 0; i < 5; i++)
        {
          Callable callable = new CallableExample();
    
          // Create the FutureTask with Callable
          randomNumberTasks[i] = new FutureTask(callable);
    
          // As it implements Runnable, create Thread
          // with FutureTask
          Thread t = new Thread(randomNumberTasks[i]);
          t.start();
        }
    
        for (int i = 0; i < 5; i++)
        {
          // As it implements Future, we can call get()
          System.out.println(randomNumberTasks[i].get());
    
          // This method blocks till the result is obtained
          // The get method can throw checked exceptions
          // like when it is interrupted. This is the reason
          // for adding the throws clause to main
        }
      }
    }
    

    输出:

    4
    2
    3
    3
    0
    

    在线程启动后与线程的所有交互都使用FutureTask对象,因为它实现了Future接口。因此,不需要存储Thread对象。使用FutureTask对象,可以取消任务,检查任务是否完成或尝试获取结果。

    这是仅使用Runnable的代码。

    // Java program to illustrate Runnable
    // for random number generation
    import java.util.Random;
    import java.util.concurrent.Callable;
    import java.util.concurrent.FutureTask;
    
    class RunnableExample implements Runnable
    {
        // Shared object to store result
        private Object result = null;
    
        public void run()
        {
            Random generator = new Random();
            Integer randomNumber = generator.nextInt(5);
    
            // As run cannot throw any Exception
            try
            {
                Thread.sleep(randomNumber * 1000);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
    
            // Store the return value in result when done
            result = randomNumber;
    
            // Wake up threads blocked on the get() method
            synchronized(this)
            {
                notifyAll();
            }
        }
    
        public synchronized Object get()
              throws InterruptedException
        {
            while (result == null)
                wait();
    
            return result;
        }
    }
    
    // Code is almost same as the previous example with a
    // few changes made to use Runnable instead of Callable
    public class RunnableTest
    {
        public static void main(String[] args) throws Exception
        {
            RunnableExample[] randomNumberTasks = new RunnableExample[5];
    
            for (int i = 0; i < 5; i++)
            {
                randomNumberTasks[i] = new RunnableExample();
                Thread t = new Thread(randomNumberTasks[i]);
                t.start();
            }
    
            for (int i = 0; i < 5; i++)
                System.out.println(randomNumberTasks[i].get());
        }
    }
    

    输出

    0
    4
    3
    1
    4
    2

     
    转载请保留页面地址:https://www.breakyizhan.com/java/4980.html