기술나눔

스레드 안전성 (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