Varias formas de implementar la comunicación entre hilos

Varias formas de implementar la comunicación entre hilos

1. Comunicación de hilo tradicional sincronizada + esperar + notificar

El objeto de monitor síncrono debe llamar a los métodos wait (), notify () y notifyAll () de la clase Object. Hay dos casos:

a) Método de sincronización, la instancia predeterminada de esta clase (esta) es el monitor de sincronización, al que se puede llamar directamente en el método de sincronización

b) El bloque de código de sincronización, el monitor de sincronización es el objeto entre paréntesis después de ser sincronizado, por lo que debe usar este objeto para llamar a estos tres métodos

 

Segundo, use la Condición para controlar el bloqueo de la comunicación del hilo + condición + espera + señal

El bloqueo reemplaza el método de sincronización o el bloque de código de sincronización, y la condición reemplaza la función del monitor de sincronización.

Bloqueo de bloqueo final privado = newReentrantLock ();

Condición final privada con = lock.newCondition ();

lock.lock (); con.await (); con.signalAll (); bloqueo y desbloqueo():

 

Tres, use la cola de bloqueo (BlockingQueue) para controlar la comunicación del hilo

La interfaz BlockingQueue se utiliza principalmente como herramienta para la sincronización de subprocesos. Cuando el productor intenta colocar un elemento en BlockingQueue, el hilo se bloquea si la cola está llena; cuando el consumidor intenta meter el elemento en BlockingQueue, el hilo se bloquea si la cola está vacía.

 

 

Varias implementaciones de comunicación entre subprocesos
Primero, debemos entender que hay dos modelos de comunicación entre subprocesos : memoria compartida y transmisión de mensajes. Estos dos modelos implementan básicamente los siguientes métodos. Analicemos una pregunta común en una entrevista:
1
Tema: Hay dos hilos A y B. El hilo A agrega la secuencia del elemento "abc" a un conjunto en secuencia, agregando un total de diez veces, cuando se agrega a la quinta vez. Se espera que el hilo B pueda recibir la notificación del hilo A, y luego el hilo B realiza operaciones comerciales relacionadas.

Método 1: utilizar la palabra clave volátil
para realizar una comunicación mutua entre subprocesos basada en la palabra clave volátil es la idea de usar memoria compartida, lo que significa aproximadamente que varios subprocesos monitorean una variable al mismo tiempo. Cuando esta variable cambia, el subproceso puede percibir y ejecutar el correspondiente Negocios. Esta es también la forma más sencilla de lograr

public class TestSync {
    // 定义一个共享变量来实现通信,它需要是volatile修饰,否则线程不能及时感知
    static volatile boolean notice = false;

    public static void main(String[] args) {
        List<String>  list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    notice = true;
            }
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
            while (true) {
                if (notice) {
                    System.out.println("线程B收到通知,开始执行自己的业务...");
                    break;
                }
            }
        });
        // 需要先启动线程B
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 再启动线程A
        threadA.start();
    }
}



El resultado de ejecución es:


Método 2: utilice los métodos wait () y notify ()
de la clase Object. Como todos sabemos, la clase Object proporciona métodos para la comunicación entre subprocesos: wait (), notify (), notifyaAl (), que son la base de la comunicación multi-thread. La idea de implementación es, naturalmente, la comunicación entre hilos.

Nota: esperar y notificar debe usarse con sincronizado, el método de espera libera el bloqueo, el método de notificación no libera el bloqueo

public class TestSync {
    public static void main(String[] args) {
        // 定义一个锁对象
        Object lock = new Object();
        List<String>  list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
            synchronized (lock) {
                for (int i = 1; i <= 10; i++) {
                    list.add("abc");
                    System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (list.size() == 5)
                        lock.notify();// 唤醒B线程
                }
            }
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
            while (true) {
                synchronized (lock) {
                    if (list.size() != 5) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("线程B收到通知,开始执行自己的业务...");
                }
            }
        });
        // 需要先启动线程B
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 再启动线程A
        threadA.start();
    }
}


El resultado es


De la captura de pantalla del resultado de la impresión se puede ver que después de que el hilo A emite la notificación de activación de notificación (), el hilo B solo comienza a ejecutarse después de ejecutar su propio negocio de hilos. Esto también muestra que el método de notificación () no libera el bloqueo y espera ( ) Método para liberar el bloqueo.

Método 3: Después de usar la clase de herramienta JUC CountDownLatch
jdk1.5, muchas clases de herramientas relacionadas con la programación concurrente se proporcionan bajo el paquete java.util.concurrent, lo que simplifica la escritura de nuestro código de programación concurrente. *** CountDownLatch *** se basa en el marco AQS , Equivalente a mantener un estado variable compartido entre hilos

public class TestSync {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        List<String>  list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    countDownLatch.countDown();
            }
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
            while (true) {
                if (list.size() != 5) {
                    try {
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程B收到通知,开始执行自己的业务...");
                break;
            }
        });
        // 需要先启动线程B
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 再启动线程A
        threadA.start();
    }
}



El resultado de ejecución es:


Use ReentrantLock con condición

public class TestSync {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        List<String> list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
            lock.lock();
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    condition.signal();

            }
            lock.unlock();
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
            lock.lock();
            if (list.size() != 5) {
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程B收到通知,开始执行自己的业务...");
            lock.unlock();
        });
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadA.start();
    }
}



El resultado de ejecución es:

Obviamente, este método no es muy bueno de usar, la escritura del código es complicada y el subproceso B no se puede ejecutar inmediatamente después de que A lo despierta porque no adquiere el bloqueo. Este método es el mismo que wait () y notify () de Object.

Método 5: LockSupport básico para el bloqueo y la activación entre subprocesos
LockSupport es una herramienta muy flexible para el bloqueo y la activación entre subprocesos. Úselo sin prestar atención a si esperar a que comience el subproceso o reactivar el subproceso, pero necesita saber el nombre del subproceso.

public class TestSync {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        // 实现线程B
        final Thread threadB = new Thread(() -> {
            if (list.size() != 5) {
                LockSupport.park();
            }
            System.out.println("线程B收到通知,开始执行自己的业务...");
        });
        // 实现线程A
        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    LockSupport.unpark(threadB);
            }
        });
        threadA.start();
        threadB.start();
    }
}



Resultado de la operación

 

13 artículos originales publicados · Me gusta 3 · Visitas 4986

Supongo que te gusta

Origin blog.csdn.net/u010919402/article/details/105446136
Recomendado
Clasificación