Hable acerca de su comprensión de cómo se comunican los subprocesos de Java

Los subprocesos múltiples y la concurrencia no son muy utilizados por algunos socios pequeños en el desarrollo normal, pero algunas entrevistas de experiencia laboral aún son fáciles de realizar. Por lo tanto, en los próximos números, resolveremos algunas preguntas comunes de entrevistas de subprocesos múltiples como referencia. .

forma de comunicación

①Sincronización

②Durante el método de sondeo

③mecanismo de espera/notificación

④Comunicación de tubería

1. Introducción
Este artículo resume mi comprensión del modo de comunicación entre subprocesos en JAVA multithreading.Discute principalmente la comunicación entre subprocesos en forma de código combinado con texto, por lo que se extraen algunos códigos de muestra en el libro.

Segundo, el método de comunicación entre subprocesos
① Sincronización La sincronización
mencionada aquí se refiere a la comunicación entre múltiples subprocesos a través de la palabra clave sincronizada.

Ejemplo de referencia:

clase pública MiObjeto {

synchronized public void methodA() {
    //do something....
}

synchronized public void methodB() {
    //do some other thing
}

}

public class ThreadA extiende Thread {

private MyObject object;

//Omita el constructor
@Override
public void run() { super.run(); object.methodA(); } }



la clase pública ThreadB extiende Thread {

private MyObject object;

//Omita el constructor
@Override
public void run() { super.run(); object.methodB(); } }



public class Run { public static void main(String[] args) { MyObject object = new MyObject();

    //线程A与线程B 持有的是同一个对象:object
    ThreadA a = new ThreadA(object);
    ThreadB b = new ThreadB(object);
    a.start();
    b.start();
}

}
Dado que el subproceso A y el subproceso B contienen el mismo objeto objeto de la clase MyObject, aunque estos dos subprocesos deben llamar a métodos diferentes, se ejecutan de forma síncrona, por ejemplo: el subproceso B debe esperar a que el subproceso A termine de ejecutar el método methodA() , puede ejecutar el método methodB(). De esta forma, el hilo A y el hilo B se comunican.

Este método es esencialmente un tipo de comunicación de "memoria compartida". Múltiples subprocesos necesitan acceder a la misma variable compartida, y quien obtiene el bloqueo (obtiene el derecho de acceso) puede ejecutarlo.

②Durante el método de sondeo El
código es el siguiente:

importar java.util.ArrayList;
importar java.util.List;

clase pública MiLista {

private List<String> list = new ArrayList<String>();
public void add() {
    list.add("elements");
}
public int size() {
    return list.size();
}

}

importar milista.MiLista;

public class ThreadA extiende Thread {

private MyList list;

public ThreadA(MyList list) {
    super();
    this.list = list;
}

@Override
public void run() {
    try {
        for (int i = 0; i < 10; i++) {
            list.add();
            System.out.println("添加了" + (i + 1) + "个元素");
            Thread.sleep(1000);
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

importar milista.MiLista;

la clase pública ThreadB extiende Thread {

private MyList list;

public ThreadB(MyList list) {
    super();
    this.list = list;
}

@Override
public void run() {
    try {
        while (true) {
            if (list.size() == 5) {
                System.out.println("==5, 线程b准备退出了");
                throw new InterruptedException();
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

importar milista.MiLista;
importar subproceso.SubprocesoA;
importar subproceso.SubprocesoB;

Prueba de clase pública {

public static void main(String[] args) {
    MyList service = new MyList();

    ThreadA a = new ThreadA(service);
    a.setName("A");
    a.start();

    ThreadB b = new ThreadB(service);
    b.setName("B");
    b.start();
}

}
De esta forma, el subproceso A sigue cambiando la condición, y el subproceso B continúa comprobando si esta condición (list.size()==5) se establece a través de la instrucción while, realizando así la comunicación entre subprocesos. Pero de esta manera desperdiciará recursos de CPU.

La razón por la que se dice que es un desperdicio de recursos es que cuando el planificador de JVM entrega la CPU al subproceso B para su ejecución, no realiza ningún trabajo "útil", sino que comprueba constantemente si una determinada condición es cierta. Al igual que en la vida real, alguien sigue mirando la pantalla del teléfono para ver si hay una llamada entrante, no: haciendo otra cosa, cuando llega una llamada, suena el timbre para avisar a TA que la llamada está llegando.

Hay otro problema con este enfoque:

Para la visibilidad de las condiciones de sondeo, para la visibilidad de la memoria, consulte: El primer punto en la comparación de volátil y sincronizado en subprocesos múltiples JAVA "Uno, la visibilidad de la palabra clave volátil"

Los subprocesos son variables locales que primero leen variables en el espacio de la pila de subprocesos locales y luego las modifican. Por lo tanto, si el subproceso B obtiene la variable de condición local cada vez, aunque otro subproceso haya cambiado la condición de sondeo, no se dará cuenta de ello, lo que también provocará un bucle infinito. Aquí recomiendo un círculo de intercambio de aprendizaje de arquitectura para todos. Guía de estudio de comunicación Pseudo Xin: 1253431195 (hay muchas preguntas y respuestas de entrevistas), que compartirá algunas grabaciones de video grabadas por arquitectos senior: Spring, MyBatis, Netty análisis de código fuente, alta concurrencia, alto rendimiento, arquitectura de microservicio distribuida The El principio de optimización del rendimiento de JVM, la arquitectura distribuida, etc. se han convertido en el sistema de conocimiento necesario para los arquitectos. También puede recibir recursos de aprendizaje gratuitos, que actualmente se están beneficiando mucho.

③mecanismo de espera/notificación El
código es el siguiente:

importar java.util.ArrayList;
importar java.util.List;

clase pública MiLista {

private static List<String> list = new ArrayList<String>();

public static void add() {
    list.add("anyString");
}

public static int size() {
    return list.size();
}

}

public class ThreadA extiende Thread {

private Object lock;

public ThreadA(Object lock) {
    super();
    this.lock = lock;
}

@Override
public void run() {
    try {
        synchronized (lock) {
            if (MyList.size() != 5) {
                System.out.println("wait begin "
                        + System.currentTimeMillis());
                lock.wait();
                System.out.println("wait end  "
                        + System.currentTimeMillis());
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

ThreadB de clase pública extiende Thread { bloqueo de objeto privado;

public ThreadB(Object lock) {
    super();
    this.lock = lock;
}

@Override
public void run() {
    try {
        synchronized (lock) {
            for (int i = 0; i < 10; i++) {
                MyList.add();
                if (MyList.size() == 5) {
                    lock.notify();
                    System.out.println("已经发出了通知");
                }
                System.out.println("添加了" + (i + 1) + "个元素!");
                Thread.sleep(1000);
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}

clase pública Ejecutar {

public static void main(String[] args) {

    try {
        Object lock = new Object();

        ThreadA a = new ThreadA(lock);
        a.start();

        Thread.sleep(50);

        ThreadB b = new ThreadB(lock);
        b.start();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}
El subproceso A espera que se cumpla una determinada condición (list.size()==5) antes de realizar la operación. El subproceso B agrega elementos a la lista y cambia el tamaño de la lista.

¿Cómo se comunican A y B? Es decir, ¿cómo sabe el hilo A que list.size() ya es 5?

Aquí se utilizan los métodos wait() y notificar() de la clase Object.

Cuando no se cumple la condición (list.size() !=5), el subproceso A llama a wait() para renunciar a la CPU y entra en estado de bloqueo. - no ocupa la CPU como ②durante el sondeo

Cuando se cumple la condición, el subproceso B llama a notificar () para notificar al subproceso A. El llamado subproceso de notificación A es para activar el subproceso A y dejar que entre en el estado ejecutable.

Uno de los beneficios de este enfoque es que aumenta la utilización de la CPU.

Pero también hay algunas desventajas: por ejemplo, el subproceso B se ejecuta primero, agrega 5 elementos a la vez y llama a notificar () para enviar una notificación, mientras que el subproceso A todavía se está ejecutando en este momento; cuando el subproceso A se ejecuta y llama a esperar (), entonces nunca puede ser despertado. Porque el subproceso B ya envió una notificación y no enviará una notificación en el futuro. Esto muestra que la notificación es demasiado temprana, lo que interrumpirá la lógica de ejecución del programa.

④La comunicación
de tubería es usar java.io.PipedInputStream y java.io.PipedOutputStream para comunicarse

No se presentará en detalle. Hay dos mecanismos de comunicación en los sistemas distribuidos: el mecanismo de memoria compartida y el mecanismo de comunicación de mensajes. Siento que la palabra clave sincronizada en el ① anterior y el sondeo while en ② "pertenecen" al mecanismo de memoria compartida, porque cuando la condición de sondeo se modifica con la palabra clave volátil, significa que juzgan esta "variable de condición compartida" Si se ha cambiado para lograr la comunicación entre procesos.

La comunicación por tubería es más como un mecanismo de paso de mensajes, es decir, un mensaje de un hilo se envía a otro a través de una tubería.

Supongo que te gusta

Origin blog.csdn.net/m0_54828003/article/details/127260365
Recomendado
Clasificación