Comment utiliser le mot clé synchronized en Java, cet exemple doit être vu !

Continuez à créer, accélérez la croissance ! C'est le sixième jour de ma participation au "Nuggets Daily New Plan · October Update Challenge", cliquez pour voir les détails de l'événement

En développement normal, le mot clé synchronized est souvent rencontré, savez-vous comment utiliser synchronized ? Cet article va vous présenter.

Nous avons deux façons d'utiliser la synchronisation :

  • utiliser la méthode synchrone
  • Utiliser des instructions ou des blocs synchronisés

utiliser la méthode synchrone

Pour rendre une méthode synchronisée, ajoutez simplement le mot clé synchronized à sa déclaration :

public class SynchronizedDemo {

    private int i = 0;

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

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

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

Comme indiqué dans le code ci-dessus, il existe trois méthodes de synchronisation :

  • ajouter()
  • du()
  • obtenirValeur()

Chaque méthode d'un même objet ne sera appelée qu'une seule fois à la fois. Par exemple, lorsqu'un thread appelle add(), les autres threads seront bloqués jusqu'à ce que le premier thread finisse de traiter la méthode add().

Utiliser des instructions ou des blocs synchronisés

    public void del(int value){

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

Dans le code ci-dessus, synchronized est ajouté avant un code {}, qui représente un bloc de code synchronisé.

Voici deux manières d'utiliser le mot clé synchronized.Introduisons brièvement les concepts liés à la synchronisation.

Qu'est-ce que la synchronisation ?

La synchronisation est un processus qui contrôle l'accès de plusieurs threads à n'importe quelle ressource partagée pour éviter des résultats incohérents. L'objectif principal de l'utilisation de la synchronisation est d'éviter un comportement incohérent des threads et d'empêcher les threads d'interférer.

En Java, le mot clé synchronized peut être utilisé pour obtenir l'effet de synchronisation. Synchronized ne peut être appliqué qu'aux méthodes et aux blocs, pas aux variables et aux classes.

Pourquoi avez-vous besoin de synchronisation ?

Regardons d'abord un bout de code :

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);
    }
}
复制代码

La valeur calculée est incrémentée de 1 à chaque fois que la méthode increment() est appelée :

2 appels en ajouteront 2, 3 appels en ajouteront 3 et 4 appels en ajouteront 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);
    }
}
复制代码

Étendons maintenant l'exemple ci-dessus et créons un thread pour appeler la méthode increment() 10 fois :

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);
    }
}
复制代码

Le résultat du calcul à ce moment est comme prévu, le résultat est 10.

Il s'agit d'une situation à un seul thread et tout est si agréable, mais est-ce vraiment le cas ? Et s'il s'agissait d'un environnement multithread ?

Démontrons maintenant la situation du multi-threading !

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 关键字的精华中的精华,请大家牢记!

おすすめ

転載: juejin.im/post/7150100677581094925