¿Cuántos usos de sincronizado?

¡Acostúmbrate a escribir juntos! Este es el segundo día de mi participación en el "Nuggets Daily New Plan · April Update Challenge", haz clic para ver los detalles del evento .

En el lenguaje Java, el principal medio para garantizar la seguridad de los subprocesos es el bloqueo, y existen dos tipos principales de bloqueos en Java: sincronizado y bloqueo. Hoy nos centraremos en varios usos de sincronizado.

Introducción

Usando sincronizado, no hay necesidad de realizar manualmente la operación de bloquear y liberar el bloqueo, solo necesitamos declarar la palabra clave sincronizada, y el nivel de JVM nos ayudará a realizar automáticamente la operación de bloquear y liberar el bloqueo. Synchronized se puede usar para decorar métodos normales, métodos estáticos y bloques de código. Veámoslos por separado.

1. Modificación de los métodos ordinarios

El uso de sincronizado para modificar métodos ordinarios es el siguiente:

/**
 * synchronized 修饰普通方法
 */
public synchronized void method() {
    // ....
}
复制代码

Cuando sincronizado modifica un método ordinario, el método modificado se llama método sincronizado, y su alcance es el método completo, y el objeto de acción es el objeto que llama al método.

2. Modificar métodos estáticos

La modificación sincronizada de los métodos estáticos es similar a la de los métodos ordinarios, su uso es el siguiente:

/**
 * synchronized 修饰静态方法
 */
public static synchronized void staticMethod() {
    // .......
}
复制代码

Cuando sincronizado modifica un método estático, su alcance es todo el programa y el bloqueo es mutuamente excluyente para todos los objetos que llaman al bloqueo.

La llamada exclusión mutua significa que solo un subproceso puede usarlo al mismo tiempo, y otros subprocesos solo pueden esperar en línea.

Decorar métodos ordinarios VS decorar métodos estáticos

Los métodos ordinarios modificados sincronizados y los métodos estáticos parecen ser lo mismo, pero son completamente diferentes.Para los métodos estáticos, el bloqueo sincronizado es global, es decir, durante todo el programa en ejecución, todos los objetos que llaman a este método estático son mutuamente excluyentes, mientras que el común El método es para el nivel de objeto, y diferentes objetos corresponden a diferentes bloqueos . Por ejemplo, el siguiente código llama al método dos veces, pero la adquisición del bloqueo es completamente diferente. El código de implementación es el siguiente:

import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SynchronizedUsage {
    public static void main(String[] args) throws InterruptedException {
        // 创建线程池同时执行任务
        ExecutorService threadPool = Executors.newFixedThreadPool(10);

        // 执行两次静态方法
        threadPool.execute(() -> {
            staticMethod();
        });
        threadPool.execute(() -> {
            staticMethod();
        });
        
        // 执行两次普通方法
        threadPool.execute(() -> {
            SynchronizedUsage usage = new SynchronizedUsage();
            usage.method();
        });
        threadPool.execute(() -> {
            SynchronizedUsage usage2 = new SynchronizedUsage();
            usage2.method();
        });
    }

    /**
     * synchronized 修饰普通方法
     * 本方法的执行需要 3s(因为有 3s 的休眠时间)
     */
    public synchronized void method() {
        System.out.println("普通方法执行时间:" + LocalDateTime.now());
        try {
            // 休眠 3s
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * synchronized 修饰静态方法
     * 本方法的执行需要 3s(因为有 3s 的休眠时间)
     */
    public static synchronized void staticMethod() {
        System.out.println("静态方法执行时间:" + LocalDateTime.now());
        try {
            // 休眠 3s
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
复制代码

Los resultados de la ejecución del programa anterior son los siguientes: imagen.pngDe los resultados anteriores, se puede ver que el bloqueo del método estático es global y está dirigido a todas las personas que llaman, mientras que el bloqueo del método ordinario está a nivel de objeto, y diferentes objetos tienen diferentes bloqueos. .

3. Modificar el bloque de código

En nuestro desarrollo diario, el método más utilizado es bloquear bloques de código en lugar de métodos, porque bloquear un método equivale a bloquear todo el método. En este caso, la granularidad del bloqueo es demasiado grande y el programa El rendimiento de ejecución se verá afectado, por lo que normalmente usaremos sincronizado para bloquear el bloque de código. Su sintaxis de implementación es la siguiente:

public void classMethod() throws InterruptedException {
    // 前置代码...
    
    // 加锁代码
    synchronized (SynchronizedUsage.class) {
        // ......
    }
    
    // 后置代码...
}
复制代码

Del código anterior, podemos ver que, en comparación con el método de modificación, el bloque de código de modificación necesita especificar manualmente el objeto de bloqueo. El objeto bloqueado generalmente se representa en la forma de this o xxx.class, como el siguiente código:

// 加锁某个类
synchronized (SynchronizedUsage.class) {
    // ......
}

// 加锁当前类对象
synchronized (this) {
    // ......
}
复制代码

esta clase VS

Usar sincronizado para bloquear esto es completamente diferente de xxx.class. Cuando esto está bloqueado, significa que el objeto actual se usa para bloquear, y cada objeto corresponde a un bloqueo; cuando se usa xxx.class para bloquear, significa que Usar un class (no una instancia de clase) para bloquear, que es a nivel de aplicación y globalmente efectivo, como se muestra en el siguiente código:

import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class SynchronizedUsageBlock {
    public static void main(String[] args) throws InterruptedException {
        // 创建线程池同时执行任务
        ExecutorService threadPool = Executors.newFixedThreadPool(10);

        // 执行两次 synchronized(this)
        threadPool.execute(() -> {
            SynchronizedUsageBlock usage = new SynchronizedUsageBlock();
            usage.thisMethod();
        });
        threadPool.execute(() -> {
            SynchronizedUsageBlock usage2 = new SynchronizedUsageBlock();
            usage2.thisMethod();
        });

        // 执行两次 synchronized(xxx.class)
        threadPool.execute(() -> {
            SynchronizedUsageBlock usage3 = new SynchronizedUsageBlock();
            usage3.classMethod();
        });
        threadPool.execute(() -> {
            SynchronizedUsageBlock usage4 = new SynchronizedUsageBlock();
            usage4.classMethod();
        });
    }

    /**
     * synchronized(this) 加锁
     * 本方法的执行需要 3s(因为有 3s 的休眠时间)
     */
    public void thisMethod() {
        synchronized (this) {
            System.out.println("synchronized(this) 加锁:" + LocalDateTime.now());
            try {
                // 休眠 3s
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * synchronized(xxx.class) 加锁
     * 本方法的执行需要 3s(因为有 3s 的休眠时间)
     */
    public void classMethod() {
        synchronized (SynchronizedUsageBlock.class) {
            System.out.println("synchronized(xxx.class) 加锁:" + LocalDateTime.now());
            try {
                // 休眠 3s
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
复制代码

El resultado de la ejecución del programa anterior es el siguiente:imagen.png

Resumir

Hay tres usos de sincronizado, que se pueden usar para modificar métodos comunes, métodos estáticos y bloques de código. El más comúnmente usado es para modificar bloques de código. Al modificar bloques de código, debe especificar un objeto de bloqueo. Este objeto de bloqueo generalmente usa this o xxx .class significa que cuando se usa this, significa que el objeto actual se usa para bloquear, y cuando se usa class, significa que cierta clase (instancia de objeto que no es de clase) se usa para bloquear, que es globalmente eficaz.

Depende de uno mismo juzgar el bien y el mal, escuchar a los demás y contar las ganancias y las pérdidas.

Cuenta oficial: análisis de las preguntas de la entrevista de Java

Colección de entrevistas: gitee.com/mydb/interv…

Supongo que te gusta

Origin juejin.im/post/7085976394676568094
Recomendado
Clasificación