java中线程安全问题举例分析
发表于:2025-01-19 作者:千家信息网编辑
千家信息网最后更新 2025年01月19日,这篇文章主要讲解了"java中线程安全问题举例分析",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"java中线程安全问题举例分析"吧!一、什么时候数据在
千家信息网最后更新 2025年01月19日java中线程安全问题举例分析
这篇文章主要讲解了"java中线程安全问题举例分析",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"java中线程安全问题举例分析"吧!
一、什么时候数据在多线程并发的环境下会存在安全问题?
三个条件:
条件1:多线程并发。
条件2:有共享数据。
条件3:共享数据有修改的行为。
满足以上3个条件之后,就会存在线程安全问题。
二、怎么解决线程安全问题?
线程排队执行。(不能并发)。用排队执行解决线程安全问题。这种机制被称为:线程同步机制。
三、银行 取钱/存钱 案例
Account 类
package ThreadSafa; /*银行账户 */ public class Account { // 账号 private String actno; // 余额 private double balance; public Account() { } public Account(String actno, double balance) { this.actno = actno; this.balance = balance; } public String getActno() { return actno; } public void setActno(String actno) { this.actno = actno; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } //取款方法 public void withdraw(double money) { // 取款之前的余额 double before = this.getBalance(); // 取款之后的余额 double after = before - money; // 更新余额 try { //模拟网络延时 更新余额不及时 百分百会出问题 Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } this.setBalance(after); }}
AccountThread 类
package ThreadSafa; public class AccountThread extends Thread { // 两个线程必须共享同一个账户对象。 private Account act; //通过构造方法传递过来账户对象 public AccountThread(Account act) { this.act = act; } @Override public void run() { double money = 5000; //取款 act.withdraw(5000); System.out.println(Thread.currentThread().getName() + "账户" + act.getActno() + "取款成功,余额" + act.getBalance()); }}
Test 类
package ThreadSafa; public class Test { public static void main(String[] args) { // 创建账户对象 Account act = new Account("act-001", 10000); //创建两个线程 Thread t1 = new AccountThread(act); Thread t2 = new AccountThread(act); //设置name t1.setName("t1"); t2.setName("t2"); //启动线程 t1.start(); t2.start(); }}
运行问题
解决方法 修改 Account 类 中的 withdraw 方法
package ThreadSafa; /*银行账户 */ public class Account { // 账号 private String actno; // 余额 private double balance; public Account() { } public Account(String actno, double balance) { this.actno = actno; this.balance = balance; } public String getActno() { return actno; } public void setActno(String actno) { this.actno = actno; } public double getBalance() { return balance; } public void setBalance(double balance) { this.balance = balance; } //取款方法 public void withdraw(double money) { // 以下这几行代码必须是线程排队的,不能并发 // 一个线程把这里的代码全部执行结束之后,另外一个线程才能进来 /* 线程同步机制的语法是: synchronized (){ // 线程同步代码块 } synchronized后面小括号中的这个"数据"是相当关键的。 这个数据必须是多线程共享的数据。才能达到多线程排队 ()中写什么? 那要看你想让那些线程同步。 假设t1、t2、t3、t4、t5,有5个线程, 你只希望t1 t2 t3排队,t4 t5 不需要排队。怎么办? 你一定要在()中写一个t1 t2 t3共享的对象。而这个 对象对于t4 t5来说不是共享的。 这里的共享对象是:账户对象 账户对象是共享的,那么this就是账户对象吧!!! 不一定是 this ,这里只要是多线程共享的那个对象就行。 */ synchronized (this) { // 取款之前的余额 double before = this.getBalance(); // 取款之后的余额 double after = before - money; // 更新余额 try { //模拟网络延时 更新余额不及时 百分百会出问题 Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } this.setBalance(after); } }}
为什么会出现线程安全问题
计算机系统资源分配的单位为进程,同一个进程中允许多个线程并发执行,并且多个线程会共享进程范围内的资源:例如内存地址。当多个线程并发访问同一个内存地址并且内存地址保存的值是可变的时候可能会发生线程安全问题,因此需要内存数据共享机制来保证线程安全问题。
对应到java服务来说,在虚拟中的共享内存地址是java的堆内存,比如以下程序中线程安全问题:
public class ThreadUnsafeDemo { private static final ExecutorService EXECUTOR_SERVICE; static { EXECUTOR_SERVICE = new ThreadPoolExecutor(100, 100, 1000 * 10, TimeUnit.SECONDS, new LinkedBlockingQueue(100), new ThreadFactory() { private AtomicLong atomicLong = new AtomicLong(1); @Override public Thread newThread(Runnable r) { return new Thread(r, "Thread-Safe-Thread-" + atomicLong.getAndIncrement()); } }); } public static void main(String[] args) throws Exception { Map params = new HashMap<>(); List futureList = new ArrayList<>(100); for (int i = 0; i < 100; i++) { futureList.add(EXECUTOR_SERVICE.submit(new CacheOpTask(params))); } for (Future future : futureList) { System.out.println("Future result:" + future.get()); } System.out.println(params); } private static class CacheOpTask implements Callable { private Map params; CacheOpTask(Map params) { this.params = params; } @Override public Integer call() { for (int i = 0; i < 100; i++) { int count = params.getOrDefault("count", 0); params.put("count", ++count); } return params.get("count"); } }}
创建100个task,每个task对map中的元素累加100此,程序执行结果为:
{count=9846}
而预期的正确结果为:
{count=10000}
至于出现这种问题的原因,下面会具体分析。
判断是否有线程安全性的一个原则是:
是否有多线程访问可变的共享变量
感谢各位的阅读,以上就是"java中线程安全问题举例分析"的内容了,经过本文的学习后,相信大家对java中线程安全问题举例分析这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!
线程
问题
安全
余额
对象
账户
数据
内存
分析
方法
条件
地址
机制
同步
更新
代码
多个
进程
银行
学习
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
安兔兔 数据库
网络安全的网络诈骗
网络安全监督检查发现的问题
北京数字化城管软件开发公司
服务器安装ansys
网络安全教训
重庆超频服务器近期价格
日本科学技术数据库
广东酷有网络技术有限公司
pg 命令恢复数据库
网络安全防护测评报告
亚洲服务器怎么注册战网
电脑服务器需要备案吗
mac连接svn服务器
有效的网络安全防御体系
centos搭建mc服务器
伊朗遭受网络安全基础设施
大型服务器的内存一般是多少
光芒服务器人真少
互联网是新一轮科技革命
移动互联网与软件开发技术
网盘系统 数据库设计
web空间跟数据库
冬奥会网络安全阶段性总结
发票本地数据库
学校校园网络安全标准
深圳任子行网络技术有限公司徐丹
steam方舟手游搜不到服务器
expdp 过滤数据库
网络技术思科