李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
13.并发编程之线程安全实例练习
Leefs
2022-10-13 PM
530℃
0条
[TOC] ### 一、卖票问题练习 > 总共有1000张票,通过循环的方式开启多个线程模拟多人买票场景 ```java /** * 卖票问题练习 */ @Slf4j(topic = "c.ExerciseSell") public class ExerciseSell { public static void main(String[] args) throws InterruptedException { //模拟多人买票 TicketWindow window = new TicketWindow(1000); //所有线程的集合 List
threadList = new ArrayList<>(); //卖出的票数统计 // List
amountList = new ArrayList<>(); //Vector是线程安全的,可以在多线程环境使用 List
amountList = new Vector<>(); for (int i = 0; i < 2000; i++){ Thread thread = new Thread(() -> { // 加个随机睡眠,确保出现问题 try { Thread.sleep(random(3)); } catch (InterruptedException e) { e.printStackTrace(); } //买票(临界区代码) 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; } //售票 //该操作涉及了对线程外部共享变量count的读写操作,需要线程安全 public synchronized int sell(int amount){ if(this.count >= amount) { this.count -= amount; return amount; }else { return 0; } } } ``` **运行结果** ``` 19:22:21.917 c.ExerciseSell [main] - 余票:0 19:22:21.924 c.ExerciseSell [main] - 卖出的票数:1000 ``` **分析** 用下面的代码行不行,为什么? ```java List
sellCount = new ArrayList<>(); ``` + 不行,因为sellCount会被多个线程共享,必须使用线程安全的实现类。 *注意:只将对count进行修改的一行代码用synchronized括起来也不行。对count大小的判断也必须是为原子操作的一部分,否则也会导致count值异常。* ### 二、转账问题练习 > 通过两个线程来模拟两个用户相互转账场景 ```java @Slf4j(topic = "c.ExerciseTransfer") 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~100 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; } //转账 //将transfer方法的方法体用同步代码块包裹,将Account.class设为锁对象。 public void transfer(Account target,int amount){ synchronized (Account.class){ if (this.money >= amount){ this.setMoney(this.getMoney() - amount); target.setMoney(target.getMoney() + amount); } } } } ``` **运行结果** ``` 19:33:02.130 c.ExerciseTransfer [main] - total:2000 ``` **分析** 这样改正行不行,为什么? ```java public synchronized void transfer(Account target, int amount) { if (this.money > amount) { this.setMoney(this.getMoney() - amount); target.setMoney(target.getMoney() + amount); } } ``` 不行,因为不同线程调用此方法,将会锁住不同的对象 *附参考原文地址* *《黑马程序员之并发编程》*
标签:
并发编程
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/2482.html
上一篇
12.并发编程之线程安全实例分析
下一篇
14.并发编程之Monitor概念介绍
取消回复
评论啦~
提交评论
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
标签云
gorm
设计模式
排序
二叉树
Tomcat
稀疏数组
Zookeeper
Spark SQL
Java
MyBatis
机器学习
Flume
JavaWEB项目搭建
容器深入研究
BurpSuite
Sentinel
队列
数学
SpringCloud
Docker
ClickHouse
查找
Spark RDD
Shiro
nginx
数据结构
SQL练习题
MyBatis-Plus
GET和POST
Quartz
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞