秒杀
需求
- 10个礼物20个客户抢
- 随机10个客户获取礼物,另外10无法获取礼物
任务类
记得给共享资源加锁
public class MyTask implements Runnable{
// 礼物列表
private ArrayList<String> gifts ;
// 用户名
private String username;
public MyTask( String username, ArrayList<String> gifts) {
this.username = username;
this.gifts = gifts;
}
@Override
public void run() {
// 模拟网络延迟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 加锁 锁对象是gifts 保证线程安全
synchronized (gifts) {
// 抢礼物
grabGift();
}
}
// 抢礼物
private void grabGift() {
if (gifts.isEmpty()) {
System.out.println("礼物已经被抢完了," + username + "没有抢到礼物");
return;
}
// 输出礼物信息 随机抢一个
System.out.println(username + "抢到了" + gifts.remove((int) (Math.random() * gifts.size()))) ;
}
}
测试
1、创建线程池
2、执行任务
3、关闭线程池
// 礼物列表 有10个礼物
ArrayList<String> gifts = new ArrayList<String>() {{
add("iPhone 12");
add("MacBook Pro");
add("iPad Pro");
add("Apple Watch");
add("AirPods Pro");
add("HomePod");
add("Apple TV");
add("Apple Music");
add("Apple Arcade");
add("Apple Fitness");
}};
// 使用线程池 模拟20个用户同时抢礼物
ExecutorService executorService = Executors.newFixedThreadPool(7);
for (int i = 0; i < 20; i++) {
executorService.execute(new MyTask("用户" + i, gifts));
}
// 关闭线程池
executorService.shutdown();
结果
双人取款机
ATM任务类
- 默认同一个账户id等同于同一个锁对象
- 取款的时候加锁,去完款就解锁
public class ATMTask implements Runnable{
private String accountId;
private double amount;
private static Map<String, Double> balanceMap = new HashMap<>();
private static final Map<String, Object> lockMap = new ConcurrentHashMap<>();
static {
balanceMap.put("1", 1000.0);
balanceMap.put("2", 1000.0);
}
public ATMTask(String accountId, double amount) {
this.accountId = accountId;
this.amount = amount;
}
private double getBalance(String accountId) {
return balanceMap.get(accountId);
}
private void updateBalance(String accountId, double amount) {
if (getBalance(accountId) < amount) {
System.out.println("账户余额不足");
return;
} else {
System.out.println("用户" + accountId + "取款成功,取款金额:" + amount);
balanceMap.put(accountId, getBalance(accountId) - amount);
// 移除锁对象
lockMap.remove(accountId);
}
}
/**
* 这个方法接受一个账户ID作为参数,
* 然后检查lockMap中是否已经存在对应该账户ID的锁对象。
* 如果不存在,它会使用putIfAbsent方法添加一个新的锁对象到lockMap中。
* 这个方法最终返回账户ID对应的锁对象。
* 这种方式确保了每个账户ID都有一个唯一的锁对象,而且这个过程是线程安全的。
* @param accountId 账户ID
* @return
*/
private static Object getLock(String accountId) {
// 如果不存在则添加
lockMap.putIfAbsent(accountId, new Object());
// 如果存在则返回
return lockMap.get(accountId);
}
@Override
public void run() {
synchronized (getLock(this.accountId)) {
updateBalance(accountId, amount);
}
}
}
线程池模拟
public static void main(String[] args) {
// 两个线程对同一个账户取款
ATMTask task1 = new ATMTask("1", 800);
ATMTask task2 = new ATMTask("1", 800);
ATMTask task3 = new ATMTask("2", 700);
ATMTask task4 = new ATMTask("2", 300);
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
// 提交任务
executor.submit(task1);
executor.submit(task2);
executor.submit(task3);
executor.submit(task4);
// 关闭线程池
executor.shutdown();
}