主程序运行1000个子线程(太少了结果不容易出来),每个子线程执行指向同一整数引用的i++,当1000个子线程运行完毕后,主线程继续运行,输出最终的i值。很显然,如果最终输出的结果i值不等于1000,那么中间i++操作一定被分解了。程序代码如下:
【版本一】
import cn.yuzhe.multi.util.AllFinished;
public class ThreadSafeClass {
public static void main(String[] args) {
//要创建的子线程数
int threadNum=1000;
//每个子线程都要引用的对象
A_Integer bb=new A_Integer();
//创建能判断所有子线程是否结束的对象,用于保证主线程能在多有子线程都结束后再执行余下的代码,
//防止主线程提前于子线程结束
AllFinished af=new AllFinished(threadNum);
//创建多个子线程对象
NoAtomicIntegerThread[] at=new NoAtomicIntegerThread[threadNum];
for(int i=0;i<threadNum;i++){
at[i]=new NoAtomicIntegerThread(bb,i,af);
}
for(int i=0;i<threadNum;i++){
at[i].start();
}
while(true){
if( af.isAllFinished() ){
System.out.println("主线程运行结束,应该获得的最终值为"+threadNum
+";实际最终值为"+bb.int_I);
break;
}
int sleep=500; //睡眠一段时间,释放时间片
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class NoAtomicIntegerThread extends Thread{
public A_Integer jj;
private int id;
private AllFinished af;
public void run() {
jj.int_I++;
af.setItemFinished(id);
// System.out.println(jj.int_I); //输出中间结果
}
public NoAtomicIntegerThread(A_Integer __j,int _id,AllFinished _af) {
super();
this.jj=__j;
this.id=_id;
this.af=_af;
}
}
class A_Integer
{
public int int_I=0;
}
这里要用到一个我自己定义的util类:
/** *//**
* @author 红楼无梦,JAVA多线程QQ群:34237757
* Date:2008-8-13 09:28
* Function: 主线程创建多个分线程,保证主线程在所有分线程都结束后才能运行余下的代码,直至结束。
*/
public class AllFinished {
private boolean[] isItemsFinished;
private int num;
public AllFinished(int itemNum) {
super();
num=itemNum;
isItemsFinished=new boolean[num];
for(int i=0;i<num;i++)
isItemsFinished[i]=false;
}
public synchronized boolean setItemFinished(int i)
{
if(i>=num)
return false;
this.isItemsFinished[i]=true;
return true;
}
public synchronized boolean isAllFinished()
{
for(int i=0;i<num;i++){
if(!this.isItemsFinished[i])
return false;
}
return true;
}
}
例子需要运行多次才能看到结果,当实际结果和理想结果不一样的时候,很显然,i++被分解了,结果的部分输入如下:
......
996
997
998
主线程运行结束,应该获得的最终值为1000;实际最终值为998。
=====================================================
【改进一】下面的代码用AtomicInteger进行了改进,使得i++操作被原子化,代码如下:
public class ThreadSafeClass {
public static void main(String[] args) {
//要创建的子线程数
int threadNum=1000;
//每个子线程都要引用的对象
AtomicInteger bb=new AtomicInteger(0);
//创建能判断所有子线程是否结束的对象,用于保证主线程能在多有子线程都结束后再执行余下的代码,
//防止主线程提前于子线程结束
AllFinished af=new AllFinished(threadNum);
//多个子线程对象
AtomicIntegerThread[] at=new AtomicIntegerThread[threadNum];
for(int i=0;i<threadNum;i++){
at[i]=new AtomicIntegerThread(bb,i,af);
}
for(int i=0;i<threadNum;i++){
at[i].start();
}
while(true){
if(af.isAllFinished()){
System.out.println("主线程运行结束,应该获得的最终值为"+threadNum
+";实际最终值为"+bb);
break;
}
int sleep=(int)(Math.random()*500);
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class AtomicIntegerThread extends Thread{
private int id;
private AllFinished af;
public AtomicInteger j;
public AtomicIntegerThread(AtomicInteger __j,int _id,AllFinished _af) {
super();
this.j=__j;
this.id=_id;
this.af=_af;
}
public void run() {
j.getAndIncrement();
af.setItemFinished(id);
System.out.println(j); //这里输出没有同步,很可能会出现不一致,这里仅仅用来测试一下,正好能证明线程将操作分解^_^
}
}
运行了多次没有出现被分解的情况,最后我把线程数从一千改成一万,又增加到十万,并且运行多次,没有出现结果和预计结果不一致的情况。十万个线程啊!如果能分解的话早把程序分得七零八碎了,呵呵。某次结果如下:
......
99994
99993
99992
主线程运行结束,应该获得的最终值为100000;实际最终值为100000
=====================================================
【改进二】群友TITAN的方法更简单,在版本一的基础上加上一小段代码
class NoAtomicIntegerThread extends Thread{
public void run() {
synchronized(jj){
jj.int_I++;
}
af.setItemFinished(id);
System.out.println(jj.int_I);
}
}
我对比了一下改进一和改进二,在十万个线程并发情况下,二者的运行速度几乎没有差别。可见JAVA类库中的AtomicInteger还是很实用的。
大伙有什么改进的方法,欢迎大家提意见。yuzhe80@126.com
******************************************************************************
后话:这里为了保证主线程在子线程都执行完后再继续执行,特意定义了一个类AllFinished,记录每个子线程是否结束,其实大可不必,因为JAVA中对线程有join函数,在main函数中改成这样就可以了。
public class ThreadSafeClass {
public static void main(String[] args) {
//要创建的子线程数
int threadNum=1000;
//每个子线程都要引用的对象
AtomicInteger bb=new AtomicInteger(0);
//多个子线程对象
AtomicIntegerThread[] at=new AtomicIntegerThread[threadNum];
for(int i=0;i<threadNum;i++){
at[i]=new AtomicIntegerThread(bb,i);
}
long last=System.currentTimeMillis();
for(int i=0;i<threadNum;i++){
at[i].start();
}
for(int i=0;i<threadNum;i++){
try {
at[i].join(); //保证主线程在所有子线程结束后再执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("主线程运行结束,应该获得的最终值为"+threadNum
+";实际最终值为"+bb+"程序运行时间"+(System.currentTimeMillis()-last)+"毫秒");
}
}
而且这样修改,在性能上有很大提升,用我自定义的类AllFinished,运行1000个线程,平均时间为2100ms,而用join函数,平均时间为1600ms,性能提升了25%。以后还是推荐大伙使用join吧^_^