Обмен технологиями

Потокобезопасность (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.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