虽然搞JAVA好多年,一直没有搞明白同步Serializable,闲来无事,做几个小程序测试一下多线程计算一个静态数:
下面的代码是我经过修改后的最终版代码。
先创建一个Persons ,用了单例模式,SET和GET方法都做了Serializable,输出方法放在单例的Person里并且做了同步,无论如何限制get方法都会有输出重复的问题,但是有一个现象,就是尽管数据有重复,但是所有的重复后就会跳过重复的数,如1,2,2,4这样的效果,最后我分析,只要程序做了单例,计算的方法是有Serializable的,其实这个变量都是递增的,之所以取出来的数据会重复,完全是因为取数据的方法取的是对象内存变量,而在多线程执行的时候,一个对象的存和取并不是同步的。
/**
* @author huang.yueyong
* @date 2010-8-15
*
*/
package test;
import java.io.Serializable;
/**
* @author huangyy
*
*/
public class Persons implements Serializable{
int flag=0;
private static Persons instant=null;
private Persons(){
}
/**
* @return the flag
*/
public synchronized int getFlag() {
return this.flag;
}
// /**
// * @param flag the flag to set
// */
public synchronized void setFlag(int flag) {
System.out.println("输入Flag:"+flag);
this.flag = flag;
}
public static Persons getPerson(){
if(instant==null){
instant=new Persons();
System.out.println("Person init!");
}
return instant;
}
}
/**
* @author huang.yueyong
* @date 2010-8-13
*
*/
package test;
/**
* @author huangyy
*
*/
public class Thread1 implements Runnable {
/**
* @param args
*/
public void run() {
while(true){
Persons per=Persons.getPerson();
per.setFlag(per.getFlag()+1);
System.out.println("线程1:"+per.getFlag());
try {
Thread.sleep(1*10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* @author huang.yueyong
* @date 2010-8-13
*
*/
package test;
/**
* @author huangyy
*
*/
public class Thread2 implements Runnable {
/**
* @param args
*/
public static void main(String[] args){
Thread1 st1=new Thread1();
Thread2 st2=new Thread2();
Thread tr1=new Thread(st1);
Thread tr2=new Thread(st2);
tr1.start();
tr2.start();
}
public void run() {
Persons per=Persons.getPerson();
while(true){
if(per.getFlag()>100){
System.exit(0);
}
per.setFlag(per.getFlag()+1);
System.out.println("线程2:"+per.getFlag());
try {
Thread.sleep(1*10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
用这个测试方法,发现单看输入的值一直都是正确的,可是输出时候虽然做了同步,但是取出来的数据仍然会有重复的情况,说明同步其实主要是针对方法而言,针对对象的同步其实没有多大的实际意义。
这个实验告诉我,我们在对变量计算的时候,如果确实需要同步,业务直接在这一个方法里完成,如果用到其它方法,需要在这个方法里,把变量当参数传出去,不要指望这个对像为你保存场景。