多个线程共享卖票窗口对象的票数属性,票数属性是共享资源。如果不对临界区代码进行原子性处理,就会存在线程安全问题。

        验证出现线程安全问题,多个线程同时买票(访问同一个窗口的票数这个共享资源),,卖出的和剩余的,之和与总票数不符。

卖票问题中存在的线程安全问题_线程安全

 

处理方法

对卖票窗口对象进行上锁:

卖票问题中存在的线程安全问题_intellij-idea_02

 没有线程安全问题的代码

package cn.itcast.n4.exercise;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Vector;

@Slf4j(topic = "c.ExerciseSell")
public class ExerciseSell {
public static void main(String[] args) throws InterruptedException {
// 模拟多人买票
TicketWindow window = new TicketWindow(1000);

// 所有线程的集合
List<Thread> threadList = new ArrayList<>();
// 卖出的票数统计
List<Integer> amountList = new Vector<>();
for (int i = 0; i < 2000; i++) {
Thread thread = new Thread(() -> {
// 买票
int amount = window.sell(random(5));
// 统计买票数
amountList.add(amount);
});
threadList.add(thread);
thread.start();
}

for (Thread thread : threadList) {
thread.join();
}

// 统计卖出的票数和剩余票数
log.debug("余票:{}",window.getCount());
log.debug("卖出的票数:{}", amountList.stream().mapToInt(i-> i).sum());
}

// Random 为线程安全
static Random random = new Random();

// 随机 1~5
public static int random(int amount) {
return random.nextInt(amount) + 1;
}
}

// 售票窗口
class TicketWindow {
private int count;

public TicketWindow(int count) {
this.count = count;
}

// 获取余票数量
public int getCount() {
return count;
}

// 售票
public synchronized int sell(int amount) {
if (this.count >= amount) {
this.count -= amount;
return amount;
} else {
return 0;
}
}
}