[Java Concurrency Tools-Collaboration] ¿Cómo utiliza Future el uso de subprocesos múltiples para lograr el programa óptimo "hervir té"?

En el artículo anterior, cómo crear correctamente un grupo de subprocesos , introdujo el método de ejecución ThreadPoolExecutor (tarea ejecutable) para enviar la tarea, a continuación se explicará el resultado devuelto de la tarea de ejecución.

1. ¿Cómo obtener el resultado de la ejecución de la tarea?

Java utiliza 3 métodos submit () proporcionados por ThreadPoolExecutor y una clase de herramienta FutureTask para obtener el resultado de la ejecución de la tarea.

1.1 Tres métodos submit ()

// 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);

Se puede ver que los valores de retorno de los tres métodos submit () son la interfaz Future, que tiene 5 métodos.

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

La diferencia entre los tres métodos de envío son parámetros diferentes, hablemos de ellos por separado.

  1. El parámetro del método es Runnable, y el método run () de la interfaz Runnable no tiene valor de retorno, por lo que este Future solo puede afirmar el final de la tarea, similar a Thread.join ().
  2. El método call () de la interfaz invocable tiene un valor de retorno, por lo que puede llamar al método get () de la interfaz Future para obtener el resultado de la ejecución de la tarea.
  3. Finalmente, la tercera forma de enviar los parámetros es Runable y result. Runable no tiene valor de retorno, pero el método get () de llamar a su interfaz Future devuelve el resultado del resultado.
    El resultado es equivalente al puente entre el subproceso principal y el subproceso, a través del cual el subproceso principal puede compartir datos.
    El siguiente es el uso clásico de su tercer método de envío: la clase de implementación de la interfaz Runnable declara una tarea de constructor parametrizada (Resultado r). Cuando se crea el objeto de Tarea, se pasa el objeto de resultado, para que pueda ejecutarse en la clase Tarea () Se realizaron varias operaciones sobre el resultado en el método.
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 ¿Cuál es la diferencia entre los métodos submit () y execute ()?

  1. Los parámetros no son los mismos. El tercer método de envío de submit () puede pasar el parámetro de resultado.
  2. El hilo ejecutado por submit () puede devolver el resultado Future, mientras que execute () no puede devolver el resultado de la ejecución.
  3. El manejo de excepciones no es el mismo, método submit (), puede manejar excepciones al método get () de Future, intente, atrape y ejecute el subproceso, si se produce una excepción durante la ejecución de la tarea, el subproceso terminará, incapaz de manejar la excepción, por lo que solo Pruebe con anticipación y capture la excepción en el método de ejecución de subprocesos.
//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 herramientas FutureTask

El futuro anterior es una interfaz, y FutureTask es realmente una clase de herramienta, tiene dos constructores:

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

De hecho, FutureTask implementa la interfaz Runnable y la interfaz Future. Por un lado, se puede enviar como una tarea al hilo para su ejecución. Por otro lado, también se puede obtener el resultado de la ejecución de la tarea. Puede ver el siguiente código de ejemplo:

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

2. Realice el programa óptimo de "quemar agua y preparar té"

El proceso óptimo de hervir agua y preparar té debería ser así: el
Inserte la descripción de la imagen aquí
problema central de la programación concurrente: división del trabajo, sincronización, exclusión mutua, lo primero que debe hacer es la división del trabajo, es decir, cómo desmontar las tareas de manera eficiente y asignarlas a hilos, de esta manera Lo mejor, por
Inserte la descripción de la imagen aquí
supuesto, puede usar la clase de herramienta CountDownLatch, join () en la clase de herramienta java anterior, y podemos usar Future para implementar:

Primero cree dos FutureTasks-ft1 y ft2, ft1 termina de lavar la tetera, hierve agua, prepara té, ft2 termina de lavar la tetera, la taza de té y las hojas de té. Por supuesto, antes de que ft1 prepare el té, ft2 debe ejecutarse.

// 创建任务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:泡茶...
上茶:龙井

Más: Deng Xin
Referencia: Geek Time

Publicado 34 artículos originales · Me gusta0 · Visitas 1089

Supongo que te gusta

Origin blog.csdn.net/qq_42634696/article/details/105527138
Recomendado
Clasificación