Cómo usar la palabra clave sincronizada en Java, ¡este ejemplo debe verse!

¡Continúe creando, acelere el crecimiento! Este es el sexto día de mi participación en el "Nuggets Daily New Plan · October Update Challenge", haz clic para ver los detalles del evento

En el desarrollo normal, la palabra clave sincronizada se encuentra a menudo. ¿Sabe cómo usar sincronizado? Este artículo te presentará.

Tenemos dos formas de usar la sincronización:

  • utilizar el método sincrónico
  • Usar sentencias o bloques sincronizados

utilizar el método sincrónico

Para sincronizar un método, simplemente agregue la palabra clave sincronizada a su declaración:

public class SynchronizedDemo {

    private int i = 0;

    public synchronized void add() {
        i++;
    }

    public synchronized void del() {
        i--;
    }

    public synchronized int getValue() {
        return i;
    }
}
复制代码

Como se muestra en el código anterior, hay tres métodos de sincronización:

  • agregar()
  • del()
  • obtenerValor()

Cada método del mismo objeto solo se llamará una vez al mismo tiempo. Por ejemplo, cuando un subproceso llama a add(), otros subprocesos se bloquearán hasta que el primer subproceso termine de procesar el método add().

Usar sentencias o bloques sincronizados

    public void del(int value){

        synchronized(this){
            this.i -= value;
        }
    }
复制代码

En el código anterior, sincronizado se agrega antes de un código {}, que representa un bloque de código sincronizado.

Las anteriores son dos formas de utilizar la palabra clavesynchronized.Introduzcamos brevemente los conceptos relacionados con la sincronización.

¿Qué es la sincronización?

La sincronización es un proceso que controla el acceso de múltiples subprocesos a cualquier recurso compartido para evitar resultados inconsistentes. El objetivo principal de usar la sincronización es evitar el comportamiento inconsistente de los subprocesos y evitar que los subprocesos interfieran.

En Java, la palabra clave sincronizada se puede usar para lograr el efecto de sincronización. Sincronizado solo se puede aplicar a métodos y bloques, no a variables y clases.

¿Por qué necesita sincronización?

Primero veamos un fragmento de código:

public class SynchronizedDemo {

    int i;

    public void increment() {
        i++;
    }

    public static void main(String[] args) {
        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
        synchronizedDemo.increment();
        System.out.println("计算值为:" + synchronizedDemo.i);
    }
}
复制代码

El valor calculado se incrementa en 1 cada vez que se llama al método increment():

2 llamadas sumarán 2, 3 llamadas sumarán 3 y 4 llamadas sumarán 4:

public class SynchronizedDemo {

    int i;

    public void increment() {
        i++;
    }

    public static void main(String[] args) {
        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
        synchronizedDemo.increment();
        synchronizedDemo.increment();
        synchronizedDemo.increment();
        synchronizedDemo.increment();
        System.out.println("计算值为:" + synchronizedDemo.i);
    }
}
复制代码

Ahora ampliemos el ejemplo anterior y creemos un hilo para llamar al método increment() 10 veces:

public class SynchronizedDemo {

    int i;

    public void increment() {
        i++;
    }

    public static void main(String[] args) {
        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();
        Thread thread = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                synchronizedDemo.increment();
            }
        });
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("计算值为:" + synchronizedDemo.i);
    }
}
复制代码

El resultado del cálculo en este momento es el que esperábamos, el resultado es 10.

Esta es una situación de un solo subproceso y todo está muy bien, pero ¿es realmente así? ¿Y si fuera un entorno multihilo?

¡Ahora demostremos la situación de subprocesos múltiples!

public class SynchronizedDemo {

    int i;

    public void increment() {
        i++;
    }

    public static void main(String[] args) {
        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();

        Thread thread1 = new Thread(() -> {
            for (int i = 1; i <= 1000; i++) {
                synchronizedDemo.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 1; i <= 1000; i++) {
                synchronizedDemo.increment();
            }
        });

        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("计算值为:" + synchronizedDemo.i);
    }
}
复制代码

如上代码,我们创建了两个线程 thread1 和 thread2,每个线程调用1000次increment(),理论上最终打印的值应该是2000,因为thread1调用increment()1000次后值会变成1000,thread2调用increment()1000次后值会变成2000.

我们执行一下,看看结果:

结果和我们想的不一样,小于2000,我们再执行一下:

结果还是小于2000.

这是为什么呢?

因为多线程支持并行处理,因此,两个线程总是有可能同时获取计数器的值,因此都得到相同的计数器值,所以在这种情况下,不是递增计数器的值两次,只增加一次。

那么,如何避免这种情况呢?

使用 synchronized 关键字即可解决。

我们只需要将increment()方法加上synchronized就可以了:

public class SynchronizedDemo {

    int i;

    public synchronized void increment() {
        i++;
    }

    public static void main(String[] args) {
        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();

        Thread thread1 = new Thread(() -> {
            for (int i = 1; i <= 1000; i++) {
                synchronizedDemo.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 1; i <= 1000; i++) {
                synchronizedDemo.increment();
            }
        });

        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("计算值为:" + synchronizedDemo.i);
    }
}
复制代码

这个时候我们再执行一下:

可以看到,值为2000.

我们把计算次数提高到10000次:

public class SynchronizedDemo {

    int i;

    public synchronized void increment() {
        i++;
    }

    public static void main(String[] args) {
        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();

        Thread thread1 = new Thread(() -> {
            for (int i = 1; i <= 10000; i++) {
                synchronizedDemo.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 1; i <= 10000; i++) {
                synchronizedDemo.increment();
            }
        });

        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("计算值为:" + synchronizedDemo.i);
    }
}
复制代码

执行结果为:

可以看出,一个小小的synchronized竟然那么简单的就解决了这个问题。

这个背后的原理就是线程1执行increment()方法时,因为有synchronized,所以会自动将此方法加锁,而此时只有线程1拥有这把锁,其他线程只能等待,直到线程1释放这把锁,线程2才能参与调用。

同理,当线程2去调用increment()时,线程2拿到锁,线程1进入等待,直到线程2释放锁,就这样,直到计算完毕,在此过程中,不会出现计算错误的情况。

总结

  • synchronized 关键字是使块或方法同步的唯一方法。
  • synchronized 关键字提供了锁的特性,它确保线程之间不会出现竞争条件。被锁定后,线程只能从主存中读取数据,读取数据后,它会刷新写操作,然后才能释放锁。
  • synchronized 关键字还有助于避免程序语句的重新排序。

以上三个特性便是synchronized 关键字的精华中的精华,请大家牢记!

Supongo que te gusta

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