Múltiples subprocesos desde la entrada hasta el avanzado (2): uso básico del bloqueo de sincronización

Referencia: "Tecnología central de programación multiproceso de Java"

Uno, seguridad de subprocesos y seguridad sin subprocesos

  • No seguro para subprocesos: cuando varios subprocesos acceden simultáneamente a variables de instancia en el mismo objeto, el resultado son lecturas sucias (aquí usamos el ejemplo de la base de datos para explicar las lecturas sucias. Las cosas aquí son equivalentes al método de ejecución en el subproceso. cuando se ejecuta el método de ejecución en el hilo)

Lectura sucia: se leen datos sucios antes de la reversión de otra transacción. Por ejemplo, durante la ejecución de la transacción B, se modifican los datos X. Antes de comprometerse, la transacción A lee X, pero la transacción B se revierte. De esta manera, la transacción A forma una lectura sucia.

Lectura no repetible: la transacción A primero lee un dato, y luego, cuando se ejecuta la lógica, la transacción B cambia este dato, y luego, cuando la transacción A lo vuelve a leer, encuentra que los datos no coinciden, lo cual es la llamada lectura no repetible.

Lectura fantasma: La transacción A primero obtiene N datos de acuerdo con el índice de condición, y luego la transacción B cambia M elementos distintos de estos N datos o agrega M datos que cumplen las condiciones de búsqueda de la transacción A, lo que hace que la transacción A se busque de nuevo y descubra que hay N + Con M piezas de datos, se produce la lectura fantasma.

  • Seguridad de subprocesos: el valor de la variable de instancia obtenida se procesa sincrónicamente y las variables del método son seguras para subprocesos

Dos, método de sincronización sincronizada

Veamos un ejemplo:

public class HasSelfPrivateNum {
    
    
    private int num = 0;
    public void addI(String username){
    
    
        try {
    
    
            if(username.equals("a")){
    
    
                num = 100;
                System.out.println("a set over");
                Thread.sleep(2000);
            }else{
    
    
                num = 200;
                System.out.println("b set over");
            }
            System.out.println("username= " + username + " num=" + num);
        }catch (InterruptedException e){
    
    
            e.printStackTrace();
        }

    }

}
public class ThreadA extends Thread{
    
    
    private HasSelfPrivateNum numRef;
    public ThreadA(HasSelfPrivateNum numRef) {
    
    
        this.numRef = numRef;
    }
    @Override
    public void run() {
    
    
        numRef.addI("a");
    }
}
public class ThreadB extends Thread{
    
    
    private HasSelfPrivateNum numRef;
    public ThreadB(HasSelfPrivateNum numRef) {
    
    
        this.numRef = numRef;
    }

    @Override
    public void run() {
    
    
        numRef.addI("b");
    }
}
public class Run {
    
    
    public static void main(String[] args) {
    
    
        HasSelfPrivateNum numRef = new HasSelfPrivateNum();
        new ThreadA(numRef).start();
        new ThreadB(numRef).start();
    }
}

HasSelfPrivateNum es un método de operación comercial. Cuando el nombre de usuario entrante es "a", num = 100 e imprime "a set over". Cuando el nombre de usuario entrante es "b", num = 200 e imprime "b" set over ", ThreadA y ThreadB son dos hilos, Run es el hilo principal

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-hotlinking. Se recomienda guardar la imagen y subirla directamente (img-6R71FfPv-1615786729413) (C: \ Users \ VSUS \ Desktop \ Notes \ Multithreading \ img \ 15.png)]

Análisis: cuando el hilo A y el hilo B se inician, el hilo A primero se apropia de la CPU, ejecuta el addI de HasSelfPrivateNum, establece num = 100 y luego ejecuta Thread.sleep (2000); el hilo A está inactivo, luego el hilo B obtiene la CPU , ingrese el método y establezca num = 200, e imprima username = b num = 200. Cuando el hilo A se despierta, no conoce el valor de num y se cambia a num = 200, por lo que genera "username = a num" = 200 "

Solo necesita agregar un bloqueo sincronizado al método addI

public class HasSelfPrivateNum {
    
    
    private int num = 0;
    synchronized public void addI(String username){
    
    
        ......
    }
}

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-hotlinking. Se recomienda guardar la imagen y subirla directamente (img-t4JNC7Bv-1615786729416) (C: \ Users \ VSUS \ Desktop \ Notes \ Multithread \ img \ 16.png)]

Análisis: cuando se inician el hilo A y el hilo B, el hilo A primero se apropia de la CPU y obtiene el bloqueo del objeto actual, ejecuta el addI de HasSelfPrivateNum, establece num = 100 y luego ejecuta Thread.sleep (2000) ;, hilo A En un estado inactivo, el subproceso B quiere ejecutar addI en este momento, pero descubre que no ha obtenido el bloqueo del objeto y solo puede esperar. Cuando el subproceso A termina de ejecutar el método addI, el bloqueo se libera y el subproceso B comienza para obtener el bloqueo y ejecutar addI

1. No se pueden sincronizar varios objetos y varias cerraduras

Cambie el método de ejecución anterior a lo siguiente:

public class Run {
    
    
    public static void main(String[] args) {
    
    
        HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
        HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
        new ThreadA(numRef1).start();
        new ThreadB(numRef2).start();
    }
}

Los resultados de la operación son:

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-hotlinking. Se recomienda guardar la imagen y subirla directamente (img-rjXJIVhV-1615786729419) (C: \ Users \ VSUS \ Desktop \ Notes \ Multithreading \ img \ 17.png)]

Se puede ver que el orden de operación es cruzado, no sincronizado. Los bloqueos sincronizados no son código, sino objetos. Si múltiples subprocesos acceden a múltiples objetos, la JVM creará múltiples bloqueos, por lo que el resultado de la ejecución es asíncrono.

2.Reingreso de bloqueo sincronizado

Bloqueo reentrante: puede volver a adquirir su propio bloqueo interno. Por ejemplo, si un hilo adquiere el bloqueo de un objeto, si el bloqueo del objeto no se ha liberado, el objeto puede obtener el bloqueo del objeto.

public class Service {
    
    
    synchronized public void service1(){
    
    
        try{
    
    
            System.out.println("begin service1 threadName=" + Thread.currentThread().getName() + " time"
                    + System.currentTimeMillis()
            );

            System.out.println("service1..");
            Thread.sleep(2000);
            System.out.println("end service1 threadName=" + Thread.currentThread().getName() + " time"
                    + System.currentTimeMillis()
            );
            service2();
        }catch (InterruptedException e){
    
    
            e.printStackTrace();
        }

    }
    synchronized public void service2(){
    
    
        try{
    
    
            System.out.println("begin service2 threadName=" + Thread.currentThread().getName() + " time"
                    + System.currentTimeMillis()
            );

            System.out.println("service2...");
            Thread.sleep(2000);
            System.out.println("end service2 threadName=" + Thread.currentThread().getName() + " time"
                    + System.currentTimeMillis()
            );
        }catch (InterruptedException e){
    
    
            e.printStackTrace();
        }

    }

}
public class MyThread2 extends Thread{
    
    
    private Service service;
    public MyThread2(Service service) {
    
    
        this.service = service;
    }

    @Override
    public void run() {
    
    
        service.service2();
    }
}
public class Run {
    
    
    public static void main(String[] args) {
    
    
        Service service = new Service();
        new MyThread1(service).start();
    }
}

En la clase Service, los métodos service1 y service2 que son todos modificados por sincronizados se crean respectivamente. Service1 llama a service2, y MyThread1 crea un hilo, que es iniciado por el principal de la clase Run. La conclusión a probar es que el hilo puede obtener el bloqueo del objeto nuevamente, es decir, se ejecutará el método service2

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-hotlinking. Se recomienda guardar la imagen y subirla directamente (img-RCbvcsSM-1615786729425) (C: \ Users \ VSUS \ Desktop \ Notes \ Multithread \ img \ 18.png)]

Pero esto no es suficiente para demostrar que incluso si el hilo adquiere el bloqueo, cree un hilo MyThread2 nuevamente

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-hotlinking. Se recomienda guardar la imagen y subirla directamente (img-rDKGpUZE-1615786729426) (C: \ Users \ VSUS \ Desktop \ Notes \ Multithread \ img \ 19.png)]

Se puede ver que el hilo 1 y el hilo 2 se ejecutan de forma asincrónica, porque el hilo 1 todavía mantiene el bloqueo del objeto cuando se ejecuta el método service2, por lo que el hilo 2 solo puede esperar.

Entonces, si un hilo adquiere el bloqueo sincronizado, entonces se sabe por lo anterior que otros hilos no pueden volver a llamar al método bloqueado por el bloqueo. ¿Puede llamar al método que no está bloqueado por el sincronizado? Modifique el código anterior: elimine el método sincronizado de service2 y elimine el método de suspensión de service2. El subproceso 1 llama al método service1 y el subproceso 2 llama al método service2.

public class Service {
    
    
    synchronized public void service1(){
    
    
        ......
    }
     public void service2(){
    
    
		......
            //Thread.sleep(2000);
		......
            );
    }

}
public class MyThread1 extends Thread{
    
    
    private Service service;
    public MyThread1(Service service) {
    
    
        this.service = service;
    }

    @Override
    public void run() {
    
    
        service.service1();
    }
}
public class MyThread2 extends Thread{
    
    
    private Service service;
    public MyThread2(Service service) {
    
    
        this.service = service;
    }

    @Override
    public void run() {
    
    
        service.service2();
    }
}

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-hotlinking. Se recomienda guardar la imagen y subirla directamente (img-m2XKTp5X-1615786729428) (C: \ Users \ VSUS \ Desktop \ notes \ multithreading \ img \ 20.png)]

Se puede ver que el hilo 1 y el hilo 2 se ejecutan de forma asincrónica, es decir, el hilo 2 puede llamar a métodos que no están bloqueados por el bloqueo de sincronización.

Análisis del proceso de ejecución anterior: El subproceso 1 comienza primero y obtiene el bloqueo y comienza a llamar al método service1. Cuando encuentra el método de suspensión, se encuentra en un estado de suspensión. La CPU asigna el subproceso 2 al segmento de tiempo y comienza a llamar al método service2 porque service2 no está sincronizado. Modificación, por lo que el hilo 2 puede llamar al método service2 y esperar hasta que el hilo 2 termine de ejecutar el método service2. Una vez que el hilo 1 se despierta, termina de ejecutar la parte posterior a la secuencia de service1 método, ¡y termina!

Conclusión importante:

Cuando el subproceso A llama al método X del objeto anyObject con la palabra clave sincronizada, el subproceso A obtiene el bloqueo del objeto donde se encuentra el método X, por lo que otros subprocesos deben esperar a que se complete el subproceso A antes de llamar al método X, y si el subproceso B llama a la declaración El método no X con la palabra clave sincronizada debe esperar a que el subproceso A ejecute el método X, es decir, se puede llamar después de que se libere el bloqueo

Nota: Cuando hay una relación de herencia de clase padre-hijo, la clase secundaria puede llamar al método de sincronización de la clase principal a través del "bloqueo reentrante", pero la sincronización no se hereda.

Tres, bloque de declaración de sincronización sincronizada

El uso de la palabra clave sincronizado para declarar el método es causado por inconvenientes en algunos casos. Por ejemplo, el subproceso A llama al método de sincronización para realizar una tarea a largo plazo, luego el subproceso B debe esperar mucho tiempo, luego el uso de sincronizado para solo bloquear algunos puede causar no- El bloque de código para problemas de seguridad de subprocesos mejorará un cierto grado de eficiencia. Además, el objeto en sí se bloquea utilizando el método sincronizado. Esto hace que si solo hay una instancia de objeto, solo se puede bloquear Se ejecutan de forma asincrónica y se usan Los bloques de instrucciones sincronizados se pueden ejecutar de forma sincrónica sin problemas de seguridad de subprocesos

public class Task {
    
    
    public void doLongTime(){
    
    
        for (int i = 1; i <= 100; i++) {
    
    
            System.out.println("noSynchronized threadName = " + Thread.currentThread().getName() + " i=" + i);
        }
        synchronized (this){
    
    
            for (int i = 1; i <= 100; i++) {
    
    
                System.out.println("Synchronized threadName = " + Thread.currentThread().getName() + " i=" + i);
            }
        }
    }

}
public class MyThread implements Runnable {
    
    
    private Task task;

    public MyThread(Task task) {
    
    
        this.task = task;
    }

    @Override
    public void run() {
    
    
        task.doLongTime();
    }
}
public class Run {
    
    
    public static void main(String[] args) {
    
    
        Task task = new Task();
        Thread t1 = new Thread(new MyThread(task));
        t1.start();
        Thread t2 = new Thread(new MyThread(task));
        t2.start();
    }
}

Cree una clase de tarea para simular una tarea a largo plazo, cree dos procesos, después del inicio, se encuentra que el bloque de código no encerrado por sincronizado se ejecuta de forma asincrónica y el código encerrado por sincronizado se ejecuta de forma sincrónica

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-hotlink. Se recomienda guardar la imagen y subirla directamente (img-wI16ulzi-1615786729431) (C: \ Users \ VSUS \ Desktop \ Notes \ Multithread \ img \ 21.png)]

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo de enlace anti-sanguijuela. Se recomienda guardar la imagen y subirla directamente (img-jKP5TkS0-1615786729432) (C: \ Users \ VSUS \ Desktop \ Notes \ Multithread \ img \ 22.png)]

Use sincronizado (esto) para bloquear el objeto actual

sincronizado (x), donde x puede ser cualquier objeto, y se pueden sacar 3 conclusiones

  • Cuando varios subprocesos ejecutan bloques de código sincronizados sincronizados (x) al mismo tiempo, muestra un efecto de sincronización
  • Cuando otros subprocesos ejecutan el método sincrónico sincronizado en el objeto x, también muestra un efecto sincrónico El
    bloque de código vivo se ejecuta de forma asincrónica y el código encerrado por sincronizado se ejecuta de forma sincrónica.

[La imagen del enlace externo se está transfiriendo ... (img-wI16ulzi-1615786729431)]

[La imagen del enlace externo se está transfiriendo ... (img-jKP5TkS0-1615786729432)]

Use sincronizado (esto) para bloquear el objeto actual

sincronizado (x), donde x puede ser cualquier objeto, y se pueden sacar 3 conclusiones

  • Cuando varios subprocesos ejecutan bloques de código sincronizados sincronizados (x) al mismo tiempo, muestra un efecto de sincronización
  • Cuando otros subprocesos ejecutan el método de sincronización sincronizado en el objeto x, también se sincroniza.
  • Cuando otros subprocesos ejecutan el bloque de código sincronizado (este) en el método del objeto x, también tiene un efecto de sincronización.

Supongo que te gusta

Origin blog.csdn.net/weixin_44706647/article/details/114829841
Recomendado
Clasificación