李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
13.并发编程之线程安全实例练习
Leefs
2022-10-13 PM
1084℃
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
NLP
4
标签云
Golang
正则表达式
并发线程
Spark Core
高并发
Java编程思想
GET和POST
排序
Kibana
JavaWEB项目搭建
JavaWeb
Tomcat
Zookeeper
Filter
Typora
数据结构和算法
SpringBoot
Java阻塞队列
Nacos
SQL练习题
散列
Hbase
HDFS
人工智能
LeetCode刷题
栈
持有对象
Linux
Java工具类
MyBatis-Plus
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭