java中线程安全问题举例分析
发表于:2025-11-10 作者:千家信息网编辑
千家信息网最后更新 2025年11月10日,这篇文章主要讲解了"java中线程安全问题举例分析",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"java中线程安全问题举例分析"吧!一、什么时候数据在
千家信息网最后更新 2025年11月10日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安全错误
数据库的锁怎样保障安全
研究网络安全文化
计算机网络技术要用什么电脑
浙江小型软件开发价格实惠
安全教育与网络安全福建
关于网络安全电子小报
网络技术在现代企业
服务器系统怎么调试
中国软件开发是做什么
网络安全法范围
数据库中的差集
中国星链网络技术
数据库查询从低到高排序
河南服务器负载均衡虚拟主机
怎么知道自己用的网络安全
请问服务器请求失败要怎么弄
jpa制作数据库表结构
巨灵财经怎么查数据库
数据库表格下载
陕西第七届网络安全宣传
gdb数据库c类受伤类
数据库修改sa密码
横向有智能检索的集成数据库
数据库sql语句优化方法
网络安全运营毕业论文
天津私人服务器系统云主机
网络安全最简单的黑板报
网络安全等级保护个人总结
网络安全三进活动主题班会
办公软件开发有什么好处
新加坡网络安全标语