23. Futuro: ¿Cómo usar multihilo para lograr el programa óptimo de "quemar agua y hacer té"? -Herramientas de concurrencia

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

ThreadPoolExecutor proporciona tres métodos submit () y una clase de herramienta FutureTask para respaldar la necesidad de obtener resultados de ejecución de tareas.

Las firmas de los tres métodos submit () son las siguientes:

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

Los 3 métodos anteriores devuelven Future. La interfaz Future tiene 5 métodos. Ambos métodos get () están bloqueando. Si la tarea no se completa cuando se llama a la tarea, el hilo que llama al método get () se bloqueará hasta Se despertará después de que se ejecute la tarea.

// 取消任务
boolean cancel(boolean mayInterruptIfRunning);
// 判断任务是否已取消  
boolean isCancelled();
// 判断任务是否已结束
boolean isDone();
// 获得任务执行结果
get();
// 获得任务执行结果,支持超时
get(long timeout, TimeUnit unit);

La diferencia entre estos tres métodos submit () es que los parámetros del método son diferentes,

  1. submit (tarea ejecutable): el parámetro es una interfaz Runnable. El método run () de la interfaz Runnable no tiene valor de retorno, por lo que el futuro devuelto por el método submit (tarea ejecutable) solo se puede usar para afirmar que la tarea ha finalizado, de forma similar a Thread. unirse ().
  2. submit (tarea invocable): el parámetro es una interfaz invocable, solo tiene un método call () y este método tiene un valor de retorno, por lo que el objeto Future devuelto por este método puede obtener el resultado de ejecución de la tarea llamando a su método get () .
  3. submit (tarea ejecutable, resultado T): suponiendo que el objeto Future devuelto por este método es f, el valor de retorno de f.get () es el resultado del parámetro pasado al método submit (). Tenga en cuenta que la clase de implementación de la interfaz Runnable declara una tarea de constructor parametrizada (Resultado r). Al crear un objeto de Tarea, se pasa un objeto de resultado, de modo que se pueden realizar varias operaciones sobre el resultado en el método run () de la clase de Tarea. . El resultado es equivalente al puente entre el subproceso principal y el subproceso, a través del cual el subproceso principal puede compartir datos.
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);
  }
}

FutureTask tool class
Dos constructores:

FutureTask(Callable<V> callable);
FutureTask(Runnable runnable, V result);

FutureTask implementa las interfaces Runnable y Future, que ThreadPoolExecutor y Thread pueden ejecutar, y se puede obtener el resultado de la ejecución.

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

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

Inserte la descripción de la imagen aquí
Se utilizan dos hilos T1 y T2 para completar el proceso de preparación de agua hirviendo y té. T1 es responsable de los tres procesos de lavado del hervidor, agua hirviendo y preparación de té. T2 es responsable de los tres procesos de lavado de la tetera, lavado de la taza de té y toma de las hojas de té. T1 está en proceso de hacer té. En este proceso, debe esperar a que T2 complete el proceso de tomar té.

Cabe señalar aquí que la tarea ft1 necesita esperar a que ft2 recupere las hojas de té antes de realizar la tarea de hacer té, por lo que ft1 necesita hacer referencia interna a ft2, y antes de ejecutar la preparación del té, llame al método get () de ft2 para esperar.

public class MyTest {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		// 创建任务T2的FutureTask
		FutureTask<String> ft2 = new FutureTask<>(new T2Task());
		// 创建任务T1的FutureTask
		FutureTask<String> ft1 = new FutureTask<>(new T1Task(ft2));
		// 线程T1执行任务ft1
		Thread thread1 = new Thread(ft1);
		thread1.start();
		// 线程T2执行任务ft2
		Thread thread2 = new Thread(ft2);
		thread2.start();
		// 等待线程T1执行结果
		String tea = ft1.get();
		System.out.println(tea);
	}
}
public class T1Task implements Callable<String> {
	FutureTask<String> ft2;
	public T1Task(FutureTask<String> ft2) {
		super();
		this.ft2 = ft2;
	}
	@Override
	public String call() throws Exception {
		System.out.println("T1 洗水壶---");
		TimeUnit.SECONDS.sleep(1);

		System.out.println("T1 烧开水---");
		TimeUnit.SECONDS.sleep(15);

		// 获得线程T2的茶叶
		String teaLeaf = ft2.get();
		System.out.println("T1 拿到茶叶:" + teaLeaf);

		System.out.println("T1 泡茶:" + teaLeaf);

		return "上茶:" + teaLeaf;
	}
}
public class T2Task implements Callable<String>{

	@Override
	public 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 "铁观音";
	}
}

Resultados finales
T1 Tetera de lavado -
T2 Tetera de lavado -
T1 Agua hirviendo -
T2 Taza de té de lavado -
T2 Tomar té
T1 Obtener té: Tieguanyin
T1 Bubble Tea: Tieguanyin
Té superior: Tieguanyin

El uso de subprocesos múltiples puede paralelizar rápidamente algunas tareas en serie para mejorar el rendimiento; si existen dependencias entre tareas, como la tarea actual depende del resultado de ejecución de la tarea anterior, este tipo de problema se puede resolver básicamente con Future. En el proceso de análisis de este tipo de problema, se recomienda que utilice un gráfico dirigido para describir las dependencias entre tareas y, al mismo tiempo, también se realiza la división de hilos, similar a la imagen de la división óptima del trabajo para hervir té. Escribir el código contra la imagen tiene la ventaja de ser más visual y menos propenso a errores.

97 artículos originales publicados · elogiados 3 · 10,000+ vistas

Supongo que te gusta

Origin blog.csdn.net/qq_39530821/article/details/102734809
Recomendado
Clasificación