【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