小弟最近遇到了一个关于多个线程共享一个数据并加锁锁定对象的程序,源程序如下:
class ShareValue
{
private int value;

void Addvalue(int v)
{
value=value+v;
}

void Delvalue(int v)
{
if(value-v>=0)
{
value=value-v;
}
else
{
value=0;
System.out.println("value小于0,无法再减!");
}
}

int total()
{
return value;
}
}class Addv extends Thread
{
ShareValue sv;
int value;

public Addv(ShareValue s,int v)
{
sv=s;
value=v;
}

public void run()
{
synchronized(sv) //锁定对ShareValue的value的操作
{
int n=sv.total();
try
{
sleep(1);
}
catch(InterruptedException e)
{
System.out.println(e);
}
sv.Addvalue(value);
if(n==0)
System.out.println("原值:"+n+"\t\t增加的值:"+value+"\t增加后的值:"+sv.total());
else
System.out.println("原值:"+n+"\t增加的值:"+value+"\t增加后的值:"+sv.total());
}
}
}

class Delv extends Thread
{
ShareValue sv;
int value;

public Delv(ShareValue s,int v)
{
sv=s;
value=v;
}

public void run()
{
int n=sv.total();
try
{
sleep(1);
}
catch(InterruptedException e)
{
System.out.println(e);
}
sv.Delvalue(value);
if(n==0)
System.out.println("原值:"+n+"\t\t减少的值:"+value+"\t减少后的值:"+sv.total());
else
System.out.println("原值:"+n+"\t减少的值:"+value+"\t减少后的值:"+sv.total());
}
}public class DataShare 
{
public static void main(String[] args)
{
ShareValue sv=new ShareValue();
Addv addv1=new Addv(sv,200);
Addv addv2=new Addv(sv,200);
Delv delv=new Delv(sv,50);
addv1.start();
delv.start();
addv2.start();
}
}用synchronized(sv)锁定对象后
addv1.start();
delv.start();
addv2.start();
上面三个语句能顺序执行了啊,可是运行的时候却得到如下的结果:
原值:0         增加的值:200   增加后的值:200
原值:0         减少的值:50    减少后的值:150
原值:150       增加的值:200   增加后的值:350而我预期得到的结果应该是下面这样的啊:
原值:0         增加的值:200   增加后的值:200
原值:200       减少的值:50    减少后的值:150
原值:150       增加的值:200   增加后的值:350想了很久发现问题应出在类Delv的方法run下的这条语句上:int n=sv.total();
就是说语句int n=sv.total();没能把在执行addv1.start();后的类ShareValue中的值value正确取出来,可是好像又有点不对啊,如果没正确得到value的话在执行delv.start();后得到的运算后的结果“减少的值:50    减少后的值:150”又没错啊,真的想不通,希望有哪位高手指点指点啊!!!兄弟分不多了,只有这么一点分了,实在对不住了,希望兄弟能谅解啊

解决方案 »

  1.   

    答:用synchronized(sv)锁定对象后,只是锁住了对sv数据的操作,但是sv.total();还是能够运行的,就是取出value。
        所以,addv1.start(); delv.start(); 两句运行之后,delv中的int n=sv.total(); 是运行了的。n=0了,但是
        sv.Delvalue(value); 要等addv1中的sv.Addvalue(value); 运行完之后再运行。所以,最后的结果是对的。 n的值先固定了,所以打印了下面的话。
    if(n==0) 
    System.out.println("原值:"+n+"\t\t减少的值:"+value+"\t减少后的值:"+sv.total()); 
      

  2.   

    在class Delv 里的
    public void run() 
    { 后面也加上:
    synchronized (sv) 这句话,那么就正确了。
      

  3.   

    我原本是按你说的是锁住两个的,后来为了验证一下在只锁一个的情况下是否能完全锁住对“value”的操作,就是想看看在对象“addv1”未释放锁之前其它对象是不是既不能对“value”写也不能读,就是对象“delv”是否除了不能执行“sv.Delvalue(value);” 外连“n=sv.total();”也不能执行,后来发现有问题。我觉得既然锁住了“value”,那么应该是既不能写也不能读的呀 
      

  4.   

     参考《Thinking in Java》第三版,关于线程,synchronized的解释。synchronized(sv) //锁定对ShareValue的value的操作 

    int n=sv.total(); 
    try ....
    }  
    “在进入此段代码之间,要获得sv的锁。” 但是在Delv里没有加锁,运行int n=sv.total(); 即使在其他的地方锁住了sv,也不用考虑有没有sv的锁,就可以执行。我运行后得到一组数据,可以证明“delv”是可以执行“sv.Delvalue(value);” 的。
    所以根本不用考虑sv有没有在其他地方被锁了。Delv代码中不用考虑sv的锁,只有加了synchronized(sv)才用考虑获得。原值:0 增加的值:200 增加后的值:200
    value小于0,无法再减!
    原值:0 减少的值:50 减少后的值:200
    原值:200 增加的值:200 增加后的值:400
      

  5.   

    代码中total()改为
    int total() 

    return value++; 

    add 原值:1 增加的值:200 增加后的值:203
    del 原值:2 减少的值:50 减少后的值:154
    add 原值:155 增加的值:200 增加后的值:356就更明显了