[Java Concurrency Tools-Collaboration] How does Future use multi-threading to achieve the optimal "boil tea" program?

In the previous article, how to correctly create a thread pool , introduced the method of ThreadPoolExecutor execute (Runnable task) to submit the task, the following will explain the return result of the execution task.

1. How to get the execution result of the task?

Java uses 3 submit () methods provided by ThreadPoolExecutor and a FutureTask tool class to obtain the task execution result.

1.1 Three submit () methods

// 1. 提交Runnable任务
Future<?> submit(Runnable task);
// 2.提交Callable任务
<T> Future<T>  submit(Callable<T> task);
// 3.提交Runnable任务及结果引用  
<T> Future<T>   submit(Runnable task, T result);

It can be seen that the return values ​​of the three submit () methods are the Future interface, which has 5 methods.

// 取消任务
boolean cancel(  boolean mayInterruptIfRunning);
// 判断任务是否已取消  
boolean isCancelled();
// 判断任务是否已结束
boolean isDone();
// 阻塞式获得任务执行结果,如果线程任务还没执行结束,那么调用get()的线程会被阻塞,直到任务执行结束才会唤醒
get();
// 阻塞式获得任务执行结果,支持超时
get(long timeout, TimeUnit unit);

The difference between the three submission methods is different parameters, let's talk about them separately.

  1. The method parameter is Runnable, and the run () method of the Runnable interface has no return value, so this Future can only assert the end of the task, similar to Thread.join ().
  2. The call () method of the Callable interface has a return value, so you can call the get () method of the Future interface to obtain the task execution result.
  3. Finally, the third way to submit the parameters is Runable and result. Runable has no return value, but the get () method of calling its Future interface returns the result of the result.
    result is equivalent to the bridge between the main thread and the sub-thread, through which the main sub-thread can share data.
    The following is the classic usage of its third submission method: The implementation class of the Runnable interface declares a parameterized constructor Task (Result r). When the Task object is created, the result object is passed in, so that it can run in the class Task () Various operations were performed on the result in the method.
ExecutorService executor   = Executors.newFixedThreadPool(1);
// 创建Result对象r
Result r = new Result();
r.setAAA(a);
// 提交任务
Future<Result> future =   executor.submit(new Task(r), r);  
Result fr = future.get();
// 下面等式成立
fr === r;
fr.getAAA() === a;
fr.getXXX() === x

class Task implements Runnable{
  Result r;
  //通过构造函数传入result
  Task(Result r){
    this.r = r;
  }
  void run() {
    //可以操作result
    a = r.getAAA();
    r.setXXX(x);
  }
}

1.2 What is the difference between submit () and execute () methods?

  1. The parameters are not the same. The third submission method of submit () can pass in the result parameter.
  2. The thread executed by submit () can return the result Future, while execute () cannot return the execution result.
  3. Exception handling is not the same, submit () method, you can handle exceptions to Future's get () method try, catch, and execute the thread, if an exception occurs during the task execution will cause the thread to terminate, unable to handle the exception, so only Try ahead and catch the exception in the thread run method.
//submit方法处理异常
		try {  
              fs.get(); 
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            } catch (ExecutionException e) {  
                executorService.shutdownNow();  
                e.printStackTrace();  
                return;  
            }  
 //execute方法处理异常,只能在Runnable接口或其他的run方法中处理异常。
  threadPoolTaskExecutor.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("sleep后");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

1.3 FutureTask tools

The above Future is an interface, and FutureTask is really a tool class, it has two constructors:

FutureTask(Callable<V> callable);
FutureTask(Runnable runnable, V result);//和上面的一样。

In fact, FutureTask implements the Runnable interface and Future interface. On the one hand, it can be submitted as a task to the thread for execution. On the other hand, the task execution result can also be obtained. You can see the following example code:

// 创建FutureTask
FutureTask<Integer> futureTask  = new FutureTask<>(()-> 1+2);
// 创建线程池
ExecutorService es =   Executors.newCachedThreadPool();
// 提交FutureTask 
es.submit(futureTask);
// 获取计算结果
Integer result = futureTask.get();

2. Realize the optimal "burning water and making tea" program

The optimal process of boiling water and brewing tea should be like this: the
Insert picture description here
core problem of concurrent programming: division of labor, synchronization, mutual exclusion, the first thing to do is division of labor, that is, how to efficiently disassemble tasks and assign them to threads, this way The best, of
Insert picture description here
course, you can use the tool class CountDownLatch, join () in the previous java tool class, and we can use Future to implement:

First create two FutureTasks-ft1 and ft2, ft1 finishes washing the kettle, boiling water, making tea, ft2 finishes washing the teapot, washing the teacup, and taking the tea leaves. Of course, before ft1 brews tea, ft2 must be executed.

// 创建任务T2的FutureTask
FutureTask<String> ft2  = new FutureTask<>(new T2Task());
// 创建任务T1的FutureTask
FutureTask<String> ft1  = new FutureTask<>(new T1Task(ft2));
// 线程T1执行任务ft1
Thread T1 = new Thread(ft1);
T1.start();
// 线程T2执行任务ft2
Thread T2 = new Thread(ft2);
T2.start();
// 等待线程T1执行结果
System.out.println(ft1.get());

// T1Task需要执行的任务:
// 洗水壶、烧开水、泡茶
class T1Task implements Callable<String>{
  FutureTask<String> ft2;
  // T1任务需要T2任务的FutureTask
  T1Task(FutureTask<String> ft2){
    this.ft2 = ft2;
  }
  @Override
  String call() throws Exception {
    System.out.println("T1:洗水壶...");
    TimeUnit.SECONDS.sleep(1);
    
    System.out.println("T1:烧开水...");
    TimeUnit.SECONDS.sleep(15);
    // 获取T2线程的茶叶  
    String tf = ft2.get();//拿到ft2的执行结果,没执行结束,阻塞等待(只是拿ft2的执行结果,并不是让ft2任务才执行)
    System.out.println("T1:拿到茶叶:"+tf);

    System.out.println("T1:泡茶...");
    return "上茶:" + tf;
  }
}
// T2Task需要执行的任务:
// 洗茶壶、洗茶杯、拿茶叶
class T2Task implements Callable<String> {
  @Override
  String call() throws Exception {
    System.out.println("T2:洗茶壶...");
    TimeUnit.SECONDS.sleep(1);

    System.out.println("T2:洗茶杯...");
    TimeUnit.SECONDS.sleep(2);

    System.out.println("T2:拿茶叶...");
    TimeUnit.SECONDS.sleep(1);
    return "龙井";
  }
}
// 一次执行结果:
T1:洗水壶...
T2:洗茶壶...
T1:烧开水...
T2:洗茶杯...
T2:拿茶叶...
T1:拿到茶叶:龙井
T1:泡茶...
上茶:龙井

More: Deng Xin
Reference: Geek Time

Published 34 original articles · Likes0 · Visits 1089

Guess you like

Origin blog.csdn.net/qq_42634696/article/details/105527138