JUC学习笔记——共享模型之管程( 六 )

经典习题分析我们给出两个经典习题分析:
/*卖票问题*/// 源码展示:public class ExerciseSell {public static void main(String[] args) {TicketWindow ticketWindow = new TicketWindow(2000);List<Thread> list = new ArrayList<>();// 用来存储买出去多少张票List<Integer> sellCount = new Vector<>();for (int i = 0; i < 2000; i++) {Thread t = new Thread(() -> {// 分析这里的竞态条件int count = ticketWindow.sell(randomAmount());sellCount.add(count);});list.add(t);t.start();}list.forEach((t) -> {try {t.join();} catch (InterruptedException e) {e.printStackTrace();}});// 买出去的票求和log.debug("selled count:{}",sellCount.stream().mapToInt(c -> c).sum());// 剩余票数log.debug("remainder count:{}", ticketWindow.getCount());}// Random 为线程安全static Random random = new Random();// 随机 1~5public static int randomAmount() {return random.nextInt(5) + 1;}}// 卖票窗口class TicketWindow {// 票数,属于共享数据private int count;public TicketWindow(int count) {this.count = count;}public int getCount() {return count;}public int sell(int amount) {if (this.count >= amount) {this.count -= amount;return amount;} else {return 0;}}}// 问题分析:TicketWindow卖票窗口的票属于共享数据,对共享数据的修改需要进行上锁处理,所以我们需要对sell使用synchronized// 修改展示:public class ExerciseSell {public static void main(String[] args) {TicketWindow ticketWindow = new TicketWindow(2000);List<Thread> list = new ArrayList<>();List<Integer> sellCount = new Vector<>();for (int i = 0; i < 2000; i++) {Thread t = new Thread(() -> {int count = ticketWindow.sell(randomAmount());sellCount.add(count);});list.add(t);t.start();}list.forEach((t) -> {try {t.join();} catch (InterruptedException e) {e.printStackTrace();}});log.debug("selled count:{}",sellCount.stream().mapToInt(c -> c).sum());log.debug("remainder count:{}", ticketWindow.getCount());}static Random random = new Random();public static int randomAmount() {return random.nextInt(5) + 1;}}class TicketWindow {private int count;public TicketWindow(int count) {this.count = count;}public int getCount() {return count;}//在方法上加一个synchronized即可public synchronized int sell(int amount) {if (this.count >= amount) {this.count -= amount;return amount;} else {return 0;}}}/*转账问题*/// 源码展示public class ExerciseTransfer {public static void main(String[] args) throws InterruptedException {Account a = new Account(1000);Account b = new Account(1000);Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {a.transfer(b, randomAmount());}}, "t1");Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {b.transfer(a, randomAmount());}}, "t2");t1.start();t2.start();t1.join();t2.join();// 查看转账2000次后的总金额log.debug("total:{}",(a.getMoney() + b.getMoney()));}// Random 为线程安全static Random random = new Random();// 随机 1~100public static int randomAmount() {return random.nextInt(100) +1;}}class Account {private int money;public Account(int money) {this.money = money;}public int getMoney() {return money;}public void setMoney(int money) {this.money = money;}public void transfer(Account target, int amount) {if (this.money > amount) {this.setMoney(this.getMoney() - amount);target.setMoney(target.getMoney() + amount);}}}// 问题分析:我们的transfer方法中存在两个对象,一个自身对象,一个被转账用户对象,所以无法使用synchronized方法但是我们可以暂时将他设置为 static synchronized 直接对账户整体进行上锁来处理问题~// 修改后代码:public class ExerciseTransfer {public static void main(String[] args) throws InterruptedException {Account a = new Account(1000);Account b = new Account(1000);Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {a.transfer(b, randomAmount());}}, "t1");Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {b.transfer(a, randomAmount());}}, "t2");t1.start();t2.start();t1.join();t2.join();log.debug("total:{}",(a.getMoney() + b.getMoney()));}static Random random = new Random();public static int randomAmount() {return random.nextInt(100) +1;}}class Account {private int money;public Account(int money) {this.money = money;}public int getMoney() {return money;}public void setMoney(int money) {this.money = money;}public static synchronized void transfer(Account target, int amount) {if (this.money > amount) {this.setMoney(this.getMoney() - amount);target.setMoney(target.getMoney() + amount);}}}

经验总结扩展阅读