Una breve discusión sobre el subproceso múltiple de Java

El subproceso múltiple de Java es una característica muy importante del lenguaje Java, que permite a los programas realizar múltiples tareas al mismo tiempo. Mediante subprocesos múltiples, un programa puede manejar múltiples tareas al mismo tiempo, acortando así el tiempo de ejecución del programa. Además, el subproceso múltiple también ayuda a utilizar procesadores de múltiples núcleos para aprovechar mejor el rendimiento del hardware de la computadora.

Entonces, ¿cómo implementamos subprocesos múltiples en Java?

ThreadHoy hablaremos de ello desde los más simples Runnablehasta los más utilizados en la actualidad.CompletableFuture

Hilo

Los subprocesos se crean en Java creando Threaduna instancia de una clase. La forma más sencilla de crear un hilo es extender Threadla clase y anular run()el método. Escriba el código que se ejecutará en run()el método y luego llame al método en el programa start()para iniciar el hilo. Por ejemplo:

public class MyThread extends Thread {
    
    
    public void run() {
    
    
        // 线程要执行的代码
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + i + "}");
        }
    }
}

public static void main(String[] args) {
    
    
	System.out.println("start");
	MyThread myThread1 = new MyThread();
	MyThread myThread2 = new MyThread();
	myThread1.start();
	myThread2.start();
	System.out.println("end");
}

Por supuesto, si le resulta problemático, puede utilizar directamente clases internas anónimas.

		// 效果是一样的
        System.out.println("start");
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "{" + i + "}");
            }
        }).start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "{" + i + "}");
            }
        }).start();
        System.out.println("end");
       

Tenga en cuenta aquí que no debe utilizar run()métodos que sincronicen los subprocesos. Al mismo tiempo, si desea ejecutar el código posterior después de que se ejecute el contenido de subprocesos múltiples, puede utilizar este método, que puede hacer que el subproceso se sincronice join(). terminar de ejecutar Luego realice las siguientes operaciones.
Por supuesto, también puedes usarlo sleep()para dejar que el hilo principal duerma por un período de tiempo, por supuesto, este tiempo de sueño es subjetivo y lo determinamos nosotros mismos, este método no es recomendado.

Ejecutable

Además, también podemos usar Runnableinterfaces para crear hilos. RunnableLa interfaz tiene un solo run()método, en el que escribes el código a ejecutar, luego Runnablepasas el objeto como parámetro al Threadconstructor de la clase y llamas start()al método para iniciar el hilo. Por ejemplo:

public class MyThread implements Runnable {
    
    
    @Override
    public void run() {
    
    
        // 线程要执行的代码
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + i + "}");
        }
    }
}

De hecho, Threades casi lo mismo. Es solo una interfaz de implementación y una clase de herencia. Si se usa directamente, no hay mucha diferencia entre los dos.
Sin embargo, algunos lugares dirán que Runnablees fácil realizar el intercambio de recursos, Threadpero no lo es Sin embargo, después de mis pruebas, creo que Threadtambién se pueden compartir recursos.

código de prueba

public class MyThread extends Thread {
    
    

    private int ticket = 5;

    @Override
    public void run() {
    
    
    	// 双检索机制——保证线程的安全
        if (ticket > 0) {
    
    
            synchronized (this) {
    
    
                if (ticket > 0) {
    
    
                    while (true) {
    
    
                        System.out.println("Thread:" + Thread.currentThread().getName() + "--Thread ticket = " + ticket--);
                        if (ticket < 0) {
    
    
                            break;
                        }
                    }
                }
            }
        }
    }

}

public class Test {
    
    
    public static void main(String[] args) {
    
    

        MyThread myThread1 = new MyThread();
        new Thread(myThread1).start();
        new Thread(myThread1).start();
        new Thread(myThread1).start();
        new Thread(myThread1).start();
        new Thread(myThread1).start();
        new Thread(myThread1).start();
    }
}

执行结果如下:
Thread:Thread-1--Thread ticket = 5
Thread:Thread-1--Thread ticket = 4
Thread:Thread-1--Thread ticket = 3
Thread:Thread-1--Thread ticket = 2
Thread:Thread-1--Thread ticket = 1
Thread:Thread-1--Thread ticket = 0

Cuando miramos Threadel código fuente, encontramos que: de hecho, Threadsolo implementa Runnablela interfaz y proporciona más métodos. Entonces no hay diferencia Threadentre y. RunnableSi hay alguna diferencia, sería la diferencia entre clases e interfaces, y la diferencia entre herencia e implementación.

Invocable (con futuro)

Para los subprocesos secundarios, a veces podemos tener dos necesidades:

  1. Obtener el resultado de ejecución del hilo secundario
  2. Obtener el estado de ejecución del subproceso secundario (éxito, fracaso, excepción)

ThreadNinguno y Runnableno cumplen estos dos requisitos, Runnablese puede obtener el estatus pero no se puede obtener el resultado, así parece Callable. CallableSe utilizan juntos Futurepara obtener resultados de ejecución de subprocesos.
( FutureEs un método de cálculo asincrónico en subprocesos múltiples de Java, que se puede utilizar para obtener los resultados de los cálculos asincrónicos durante la ejecución de la tarea)

public class MyThread implements Callable<Integer> {
    
    

    private Integer number;

    public Integer getNumber(){
    
    return number;}

    public MyThread (Integer number) {
    
    
        this.number = number;
    }

    @Override
    public Integer call() throws Exception {
    
    
        int result = 0;
        for (int i = 0; i < number; i++) {
    
    
            result ++;
            System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + result + "}");
        }
        return result;
    }
}

    public static void main(String[] args) throws Exception {
    
    
        System.out.println("start");
        MyThread myThread1= new MyThread (10);
        MyThread myThread2= new MyThread (15);
        //使用Executors工厂类创建一个简单的线程池(线程数:2)
        ExecutorService executor = Executors.newFixedThreadPool(2);
		// 将任务提交给线程池	
        Future<Integer> submit1 = executor.submit(myThread1);
        Future<Integer> submit2 = executor.submit(myThread2);
        // 获取任务的执行结果
        System.err.println(submit1.get());
        System.err.println(submit2.get());
        executor.shutdown();
        System.out.println("end");
    }

En este ejemplo, implementamos MyThreadla clase, que implementa Callablela interfaz y anula call()los métodos. En call()el método, simulamos una tarea de larga duración y devolvemos un resultado de cálculo como resultado de la ejecución. En el método principal, primero creamos un grupo de subprocesos con un número fijo de subprocesos ExecutorServicey luego pasamos MyThreadla instancia al submitmétodo para enviar la tarea y obtener un Futureobjeto de tipo. Al llamar get()al método de este objeto, podemos obtener los resultados de ejecución de la tarea. Finalmente, cerramos el grupo de subprocesos.

Cabe señalar que dado que el Futuremétodo llamado get()está bloqueando, es posible que debamos procesarlo try-catch. Además, después de completar la tarea, debemos cerrar el grupo de subprocesos para liberar los recursos.

Este es un ejemplo de uso simple Callable, pero Futurese pueden lograr cálculos simultáneos más complejos combinando múltiples objetos.

Futuro

Por supuesto, Futuretambién se puede utilizar solo.
Cuando está en uso Future, puede llamar get()a un método para bloquear la espera de que se ejecute la tarea y obtener el resultado del cálculo; también puede llamar a isDone()un método para determinar si la tarea se ha completado; si ocurre una excepción durante la ejecución de la tarea, puede get()obtener la información de excepción llamando al método

public static void main(String[] args) throws Exception {
    
    
        System.out.println("start");
        
        ExecutorService executor = Executors.newFixedThreadPool(2);
        Future future1 = executor.submit(() -> {
    
    
            int result = 0;
        for (int i = 0; i < number; i++) {
    
    
            result ++;
            System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + result + "}");
        }
        return result;
        });
		Future future2 = executor.submit(() -> {
    
    
            int result = 0;
        for (int i = 0; i < number; i++) {
    
    
            result ++;
            System.out.println("id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName() + "====={" + result + "}");
        }
        return result;
        });
        String result1 = (String) future1.get();
        String result2 = (String) future2.get();
        System.out.println(result1);
		System.out.println(result2);
        executor.shutdown();
        System.out.println("end");
    }

De hecho, es lo mismo que el método anterior, excepto que call()la lógica de negocios en el método es redundante en el código principal y el acoplamiento es mayor. Si solo se pueden usar algunos códigos simples, si es complicado, se recomienda usarlos callablejuntos para comparar. bueno

Futuro Completable

CompletableFuturees una característica muy útil introducida en Java 8 que nos permite manejar operaciones asincrónicas más fácilmente, especialmente cuando estas operaciones involucran múltiples etapas.
CompletableFutureHeredado de la Futureinterfaz, los resultados del cálculo se pueden obtener una vez completado el cálculo, por lo que también tiene Futurelas características de. Además, proporciona algunas otras características:

  1. Funciones de ejecución asincrónica y ejecución en serie.
  2. El mecanismo de observador se puede activar después de ejecutar la tarea.
  3. Se pueden combinar y ejecutar dos tareas juntas para manejar escenarios asincrónicos más complejos.

Hoy en día, es común utilizar una lógica más compleja en el desarrollo.CompletableFuture

crearCompletableFuture

Podemos usar runAsync()el método para ejecutar directamente de forma asincrónica.

CompletableFuture.runAsync(() -> {
    
    
    // 在这里执行一些耗时的操作
   
});

CompletableFutureEl resultado del procesamiento asincrónico.

Podemos usar CompletableFutureel método estático supplyAsync()para crear una ejecución asincrónica CompletableFuturey proporcionar una lambdaexpresión como generador de cálculo:

CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
    
    
    // 在这里执行一些耗时的操作
    return "some result";
});

completableFuture.thenAccept(result -> {
    
    
    System.out.println("Got result: " + result);
});
// 这里可以继续执行其他任务,completableFuture将在后台继续执行并在完成后触发回调函数。

Operar dos o másCompletableFuture

Si necesitamos esperar a que CompletableFuturese completen varias tareas antes de ejecutar ciertas tareas, entonces podemos usar CompletableFuturemétodos estáticos allOf()o anyOf():

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    
    
    // 在这里执行一些耗时的操作
    return "Result of future 1";
});

CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
    
    
    // 在这里执行一些耗时的操作
    return "Result of future 2";
});

CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2);

allFutures.thenRun(() -> {
    
    
	//这边join和get方法都可以 只是抛出异常的区别
    String result1 = future1.join();
    String result2 = future2.join();
    System.out.println("Got both results: " + result1 + " and " + result2);
});

CompletableFutureEl grupo de subprocesos se utiliza de forma predeterminada ForkJoinPool. Si desea definir un grupo de subprocesos usted mismo, puede crear Executorsuno directamente. Sin embargo, Executorses una clase de fábrica que proporciona algunos métodos estáticos para crear grupos de subprocesos, no un grupo de subprocesos en sí, por lo que el subproceso no se puede configurar Parámetros detallados como: número de subprocesos principales, número máximo de subprocesos, política de rechazo, etc. Si necesita configurar estos parámetros, debe ThreadPoolExecutorcrear manualmente el grupo de subprocesos utilizando el

    /**
     * public ThreadPoolExecutor(int corePoolSize,
     *                               int maximumPoolSize,
     *                               long keepAliveTime,
     *                               TimeUnit unit,
     *                               BlockingQueue<Runnable> workQueue,
     *                               ThreadFactory threadFactory,
     *                               RejectedExecutionHandler handler) {}
     * 1. corePoolSize:核心线程数;当提交的任务数大于 corePoolSize 时,线程池会自动扩容。
     *
     * 2. maximumPoolSize:线程池最大线程数;当活动线程数达到该值,并且 workQueue 队列已满,则执行拒绝策略。
     *
     * 3. keepAliveTime:线程空闲超时时间,超过该时间则进行回收。
     *
     * 4. unit:keepAliveTime 的时间单位。
     *
     * 5. workQueue:任务阻塞队列,用于存储提交但尚未执行的任务。
     *
     * 6. threadFactory:线程工厂,用于创建线程。
     *
     * 7. handler:拒绝策略,当线程数量已经达到 maximumPoolSize 并且队列已满时,采取的策略。
     */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
                10,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());

Hay springbootun método que también se usa con mucha frecuencia: @asyncsolo necesita agregar esta anotación al método y agregarla a la clase de inicio @Enableasyncpara implementar directamente operaciones de excepción de subprocesos múltiples. Sin embargo, este método de subprocesos múltiples sigue siendo algo diferente del Como se mencionó anteriormente, la diferencia es @asyncque cuando se llama a un método, se realizan operaciones asincrónicas de subprocesos múltiples, pero la lógica de negocios en el método aún es sincrónica, por lo que se pueden usar juntas normalmente (por supuesto, también puede extraer ComplatableFuturela lógica de negocios). en el medio y agrega un @asyncjajajaja) )

El mecanismo de subprocesos múltiples de Java es una parte indispensable de la programación Java. Comprender y familiarizarse con las capacidades de programación de subprocesos múltiples de Java mejorará en gran medida la eficiencia del trabajo y el nivel de programación de los programadores.

Supongo que te gusta

Origin blog.csdn.net/qq_43649799/article/details/130440008
Recomendado
Clasificación