【Java线程】i++的线程安全性实验
【实验目的】
验证i++方式生成序列号在多线程环境下的不确定性。
【实验类】
SnGenerator类,用于i++方式生成序列号
package com.hy.lab.nosynchonized; public class SnGenerator { int i=0; // 此函数如不加synchronized则线程不安全,生成的序列号可能重复 public int getSn(){ return i++; } }
TestTH类,继承自Thread类,用于连续取得100个序列号
package com.hy.lab.nosynchonized; import java.util.List; import java.util.concurrent.CountDownLatch; public class TestTh extends Thread{ List list; SnGenerator snGenerator; CountDownLatch cdl; public TestTh(List list, SnGenerator snGenerator, CountDownLatch cdl){ this.list=list; this.snGenerator=snGenerator; this.cdl=cdl; } public void run(){ for(int i=0;i<100;i++){ list.add(snGenerator.getSn()); } cdl.countDown(); } }
Test类,用于将诸类启动并输出结果:
package com.hy.lab.nosynchonized; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.concurrent.CountDownLatch; public class Test { public static void main(String[] args) throws Exception{ // 容纳序列号的列表 List<Integer> list=new ArrayList<>(); list= Collections.synchronizedList(list); // 线程不安全的序列号生成器 SnGenerator snGenerator=new SnGenerator(); CountDownLatch cdl=new CountDownLatch(2); // 启动两个线程各取100个序列号 TestTh th1=new TestTh(list,snGenerator,cdl); th1.start(); TestTh th2=new TestTh(list,snGenerator,cdl); th2.start(); cdl.await(); int listSize=list.size(); // 存放序列号的哈希Set HashSet<Integer> set=new HashSet<>(); for(Integer i:list){ set.add(i); } int setSize=set.size(); // 有一定几率列表长度和哈希set长度不等, // 线程不安全的情况下哈希set长度总是小于等于列表长度,这说明列表中存在重复的序列号 String msg=String.format("listSize=%d ,setSize=%d",listSize,setSize); System.out.println(msg); } }
【预期值】
运行Test类十次,每次列表长度和哈希Set长度都是200.
【实际值】
运行Test类十次,两次列表长度大于哈希Set长度.
listSize=200 ,setSize=200 listSize=200 ,setSize=200 listSize=200 ,setSize=199 listSize=200 ,setSize=200 listSize=200 ,setSize=200 listSize=200 ,setSize=200 listSize=200 ,setSize=200 listSize=200 ,setSize=199 listSize=200 ,setSize=200 listSize=200 ,setSize=200
【结论】
i++实际分为取值,累加,设值三步,后入线程可能会修改先入线程得到的i值。
【改善方法】
将SnGnerator类的getSn函数加上synchronized修饰
public class SnGenerator { int i=0; public synchronized int getSn(){ return i++; } }
END
分类:
Java.线程/线程池
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
2021-08-22 【oracle】常用表操作语句
2021-08-22 【oracle sql plus】清屏命令 clear screen
2021-08-22 【oracle】取余函数mod
2021-08-22 【Swing】使用lamda表达式简化事件响应的模板代码
2017-08-22 【Canvas与图标】对数图标 120*120
2013-08-22 【Canvas与函数极值】函数f(x)=((x+3)^2+1)^0.5+((x-5)^2+4)^0.5 的值域是?