[Java multiproceso] Cómo pasar datos a un hilo y dejar que el hilo devuelva datos

1. Cómo pasar datos al hilo

1. Pasar datos a través del método de construcción

Al crear un hilo, debe crear una instancia de la clase Thread o sus subclases. Entonces, antes de que pueda comenzar, 线程类的构造方法los datos llaman al método en subprocesos. Y use los datos entrantes 成员变量接收para que el cuerpo del hilo pueda usarlos

/**
 * TODO 测试线程传递参数1-通过构造方法传递数据
 */
public class TestThreadPassParam1 extends Thread {
    
    
    //用于接受构造方法传进来的数据
    private String name;

	//构造方法
    public TestThreadPassParam1(String name) {
    
    
        this.name = name;
    }
    
    //线程体
    @Override
    public void run() {
    
    
        System.out.println("hello " + name);
    }
    
    public static void main(String[] args) {
    
    
        Thread thread = new TestThreadPassParam1("world");
        thread.start();
    }
}

Si los datos se pasan a través del método de construcción, los datos ya están en su lugar antes de que se ejecute el subproceso, por lo que no hará que los datos se pasen después de que se ejecute el subproceso. Si desea pasar datos más complejos, puede pasar los datos a través de métodos de clase o variables de clase.

2. Pasar datos a través de variables y métodos.

Generalmente hay dos oportunidades para pasar datos a un objeto. La primera oportunidad es pasar los datos a través del método de construcción cuando se crea el objeto. La otra oportunidad es definir una serie de métodos públicos o variables en la clase (también llamado Es un campo). Luego, después de crear el objeto, asigne valores uno por uno a través de la instancia del objeto. El siguiente código es una versión revisada de la clase MyThread1, usando un método setName para establecer la variable de nombre:

/**
 * @Description TODO 测试线程传递参数2-通过变量和方法传递数据
 */
public class TestThreadPassParam2 extends Thread {
    
    
   //传递参数
    private String food;

    public void setFood(String food) {
    
    
        this.food = food;
    }

	//线程体
    @Override
    public void run() {
    
    
        System.out.println("吃" + food);
    }

    public static void main(String[] args) {
    
    
        TestThreadPassParam2 myThread = new TestThreadPassParam2();
        myThread.setFood("包子");

        Thread thread = new Thread(myThread);
        thread.start();
    }
}

3. Pasar datos a través de la función de devolución de llamada

Los dos métodos anteriores para pasar datos a subprocesos son los más utilizados. Están en 主线程main (), 主动los datos entrantes 线程类son. Para el hilo, es pasivo recibir estos datos. Sin embargo, en algunas aplicaciones, los datos deben obtenerse dinámicamente durante el proceso de ejecución del hilo.

Por ejemplo: se generan tres números aleatorios en el método de ejecución del siguiente código, y luego la suma de estos tres números aleatorios se calcula mediante el método de proceso de la clase Trabajo y el resultado se devuelve mediante el valor de la clase Datos. Como se puede ver en este ejemplo, antes de devolver el valor, se deben obtener tres números aleatorios. En otras palabras, este valor no se puede pasar a la clase de subproceso de antemano.

import java.util.Random;
/**
 * @Description TODO 测试线程传递参数3-通过回调函数传递数据
 */
public class TestThreadPassParam3 extends Thread {
    
    
    //传递参数
    private Work work;

    //构造方法
    public TestThreadPassParam3(Work work) {
    
    
        this.work = work;
    }

    //线程体
    public void run() {
    
    
        //声明Data对象
        Data data = new Data();

        //声明随机对象
        Random random = new Random();
        int n1 = random.nextInt(1000);//随机数1
        int n2 = random.nextInt(2000);//随机数2
        int n3 = random.nextInt(3000);//随机数3

        //线程内调用work对象的.process方法
        work.process(data, n1, n2, n3);   // 使用回调函数

        System.out.println(n1 + "+" + n2 + "+" + n3 + "=" + data.value);
    }

    public static void main(String[] args) {
    
    
        Thread thread = new TestThreadPassParam3(new Work());
        thread.start();
    }
}

//计数器
class Data {
    
    
    public int value = 0;
}

//在线程内回调,用于将numbers的值累加到Data.value
class Work {
    
    
    public void process(Data data, Integer... numbers) {
    
    
        for (Integer n : numbers) {
    
    
            data.value += n;
        }
    }
}

Los del código anterior process方法se denominan funciones de devolución de llamada . Esencialmente, la función de devolución de llamada es la función de evento . El proceso de llamar a la función de devolución de llamada es solo eso 最原始的触发事件的过程. En este ejemplo, llamar process方法para obtener los datos equivale a在run方法中引发了一个事件。

2. Cómo permitir que el hilo devuelva datos

Devolver datos de un hilo es similar a pasar datos a un hilo. Los datos también se pueden devolver a través de los miembros de la clase y las funciones de devolución de llamada, pero existen algunas diferencias entre los miembros de la clase en la devolución y el paso de datos.

1. El hilo principal espera

El uso de este método para devolver datos requiere que el hilo se complete antes de que los datos se puedan obtener a través de variables o métodos de clase.

/**
 * TODO 测试线程返回数据1-通过主线程等待返回数据
 */
public class TestThreadReturnData1 extends Thread {
    
    
    private Count count;

    TestThreadReturnData1(Count count) {
    
    
        this.count = count;
    }

    @Override
    public void run() {
    
    
        for (int i =0 ;i<10;i++) {
    
    
            this.count.setValue(this.count.getValue()+1);
        }
    }

    public static void main(String[] args) throws InterruptedException {
    
    
        Count count = new Count();
        Thread thread = new Thread(new TestThreadReturnData1(count));
        thread.start();

        // 获取子线程的返回值:主线程等待法
        while (count.getValue() == 0) {
    
    
            Thread.sleep(1000);
        }
        System.out.println(count.getValue());
        System.out.println("主线程执行完毕");
    }
}

//计数器
@Data
class Count {
    
    
    public int value = 0;
}

Inserte la descripción de la imagen aquí

2. El método Join bloquea el hilo actual para esperar a que se complete la ejecución del hilo secundario

使用join方法可以让子线程执行完毕后再执行主线程,本质和通过循环判断一样

import lombok.Data;

/**
 * @Description TODO 测试线程返回数据1-通过Join方法阻塞当前线程以等待子线程执行完毕返回数据
 */
public class TestThreadReturnData2 extends Thread {
    
    
    private Count2 count;

    TestThreadReturnData2(Count2 count) {
    
    
        this.count = count;
    }

    @Override
    public void run() {
    
    
        for (int i =0 ;i<10;i++) {
    
    
            this.count.setValue(this.count.getValue()+1);
        }
    }

    public static void main(String[] args) throws InterruptedException {
    
    
        Count2 count = new Count2();
        Thread thread = new Thread(new TestThreadReturnData2(count));
        thread.start();

        // 获取子线程的返回值:Thread的join方法来阻塞主线程,直到子线程返回
        thread.join();

        System.out.println(count.getValue());
        System.out.println("主线程执行完毕");
    }
}

//计数器
@Data
class Count2 {
    
    
    public int value = 0;
}

Inserte la descripción de la imagen aquí

3. Utilice la interfaz invocable y FutureTask

La interfaz invocable se agregó en JDK1.5. Implementar esta interfaz y reescribir el método call () también puede crear un nuevo hilo, ¡y este método tiene un valor de retorno!

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableAndFutureReturnData implements Callable<String> {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        /***
         * FutureTask 实现了 Runnable接口,所以新建线程的时候可以传入FutureTask
         * FutureTask重写的run方法中实际是调用了Callable接口在call()方法,所以执行线程的时候回执行call方法的内容
         */

        FutureTask<String> task = new FutureTask<String>(new CallableAndFutureReturnData());
        new Thread(task).start();

        if (!task.isDone()) {
    
    
            System.out.println("task has not finished, please wait!");
        }

        System.out.println("task return: " + task.get());
    }

    @Override
    public String call() throws Exception {
    
    
        String value = "over";
        System.out.println("Ready to work");
        Thread.currentThread().sleep(5000);
        System.out.println("task done");
        return value;
    }
}

Inserte la descripción de la imagen aquí
Aquí es necesario prestar atención a los dos métodos de FutrueTask:

  • isDone : use la variable de estado para determinar si el método de llamada se ha ejecutado
  • get : si el método de llamada se ha ejecutado, devuelve el valor de retorno del método de llamada, y si el método de llamada no se ejecuta, seguirá bloqueando

4. Usar grupo de subprocesos

public class ThreadPoolDemo {
    
    
    public static void main(String[] args) {
    
    
        //创建线程池
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        //通过线程池管理线程MyCallable
        Future<String> future = newCachedThreadPool.submit(new MyCallable());

        //如果提交的任务未完成
        if (!future.isDone()) {
    
    
            System.out.println("task has not finished, please wait!");
        }

        try {
    
    
            System.out.println(future.get());
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (ExecutionException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //关闭线程池
            newCachedThreadPool.shutdown();
        }
    }
}

class MyCallable implements Callable<String> {
    
    
    //线程体
    @Override
    public String call() throws Exception {
    
    
        String value = "over";
        System.out.println("Ready to work");
        Thread.currentThread().sleep(5000);
        System.out.println("task done");
        return value;
    }
}

Supongo que te gusta

Origin blog.csdn.net/qq877728715/article/details/107003268
Recomendado
Clasificación