2013-02-27 251 views
5

article谈到Java的“synchronized”关键字。Java线程“同步”

... 
    private int foo; 
    public synchronized int getFoo() { return foo; } 
    public synchronized void setFoo(int f) { foo = f; } 

如果主叫方希望增加foo的属性,下面的代码,这样做是不是线程安全的:

... 
    setFoo(getFoo() + 1); 

如果两个线程试图在同一时间递增foo,那么结果可能是foo的价值根据时间而增加1或2。

现在,我的问题:

上setFoo为什么不 “同步”()防止上述粗体 线?

回答

6

这是check-then-act竞争条件的一个例子。

情景可能发生类似如下:

Thread-1 getFoo() returns 0 
Thread-2 getFoo() returns 0 
Thread-2 setFoo(1) 
Thread-1 setFoo(1) 

这意味着,两个线程试图增加富,但它仅递增一次的效果。

当其他答案已经确定时,同步增量与同一个对象上的同步块锁定getFoo()和setFoo()将阻止这种竞争条件,因为线程将无法像上面那样交错。

6

,因为您选择保证没有其他人越来越富与你们一起,并没有其他人设置foo的背部,除了你,但你不是你叫之间保证没人设法进入和退出(或只是) get()和你的呼唤()设置

你可以认为代码完全等同于这一点:

int temp = getFoo(); //safe method 
temp = temp+1; //not protected here - im not holding any locks ... 
setFoo(temp); //safe method 
4

在这两种方法的​​关键字并不能使它线程安全的,因为一个线程可能请致电getFoo,然后另一个线程可以调用getFoo,他们每个人都得到相同的结果。然后他们每个人都加1,然后拨打setFoo,最终结果是foo只会增加一次,而不是两次。正如你的文章指出的,这是一个竞赛条件

为了使线程安全,读取和写入必须在同一个同步块中,而不需要单独的get和set方法。

public synchronized void addFoo(int addend) 
{ 
    foo += addend; 
} 
+0

'然后他们每个人都加一个并调用setFoo,最终的结果是foo只增加一次,而不是两次。为什么? – 2013-02-27 18:15:07

+0

因为每个线程都将'foo'更新为相同的值。例如,他们每个人都得到值2,他们每个人都加1得到3,然后他们每个人都将值设置为3. – rgettman 2013-02-27 18:19:43

+0

如果我理解正确,那么你的陈述不应该是'那么他们每个人都添加一个并调用setFoo,最终结果是foo获得**两次** **?' – 2013-02-27 18:45:18

1

代码中的陷阱主要是它似乎getFoo将被称为“里面”setFoo。的

setFoo(){ 
    //getFoo(); 
    //... 
} 

类这是不正确,因为在现实中getFoo调用setFoo之前被调用。下面是示例,说明它:

public static int foo(int i) { 
    System.out.print("FOO!"); 
    return i; 
} 

public static int bar(int i) { 
    System.out.print("BAR!"); 
    return i; 
} 

public static void main(String[] args) throws Exception { 
    System.out.println(foo(bar(1))); 
} 

输出:

BAR!FOO!1 

正如你可以看到barfoo之前调用。所以在你的情况下,有可能两个(或更多)线程调用getFoo,它们会在调用setFoo之前返回当前值。在这种情况下,他们都将拥有相同的价值,可以说,0时,他们会打电话setFoo他们都将其设置为1

0

做到这一点代码帮助?

class C { 
    private int foo; 
    public int getFoo() { return foo; } 
    public void setFoo(int f) { foo = f; } 
} 

C myC = new C(); 
synchronized(myC) { 
    int foo = myC.getFoo(); 
    myC.setFoo(foo + 1); 
} 
println(myC.foo); 
+0

所以重点是, myC.setFoo(1)'可以在'println(myC.getFoo())'之前调用? – 2013-02-27 19:02:27