技術共有

スレッド セーフ (2) 同期、ロック アップグレード、オブジェクト メモリ構造の基本的な実装原理

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. 基本的な使い方

  • 次のようなシナリオがあるとします。20 人のユーザーが一緒に 10 枚のチケットを獲得します

1.1 ロックを使用しないコードの実装

public class TicketDemo {
   

    // 票总数
    private int ticketNum = 10;

    /**
     * 抢票
     */
    public void getTicket() {
   
        if (ticketNum <= 0) {
   
            return;
        }
        System.out.println(Thread.currentThread().getName() + " 抢到一张票,剩余:" + ticketNum);
        // 非原子性操作
        ticketNum--;
    }

    /**
     * 测试:20个人抢一张票
     */
    public static void main(String[] args) {
   
        TicketDemo ticketDemo = new TicketDemo();
        for (int i = 0; i < 20; i++) {
   
            new Thread(ticketDemo::getTicket).start();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

結果:

表示されているのがわかります 売られすぎ問題 , チケットは合計 10 枚あり、20 個のスレッドが一緒にチケットを取得すると、11 人がチケットを取得したように見えます。これは、両方のスレッドが同時に if チェックに合格した場合に発生するためです。

したがって、チケット操作をロックして、1 つのスレッドだけがチケット控除を同時に確認および操作できるようにする必要があります。

1.2 ロックコードの実装

public class TicketDemo {
   

    // 锁
    private static Object lock = new Object();

    // 票总数
    private int ticketNum = 10;

    /**
     * 抢票
     */
    public void getTicket() {
   
        synchronized (lock) {
   
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16