2016-09-06 70 views
1

我想了解Java中的“synchronized block”。我写了非常基本的代码,看看如果我锁定并更改thread_1中的对象并通过另一种方法从另一个thread_2(竞争条件)访问它,会发生什么情况。但是我很难理解这种行为,因为我期待Thread_1会先改变值,然后Thread_2会访问新值,但结果并不如我预期的那样。Java中的对象锁

public class Example { 

public static void main(String[] args){ 

    final Counter counter = new Counter(); 

    Thread threadA = new Thread(new Runnable() { 

    @Override 
    public void run() { 
     System.out.println("THREAD_1_START"); 
     counter.add(1); 
     System.out.println("THREAD_1_END"); 
    } 
    }); 
    Thread threadB = new Thread(new Runnable() { 

    @Override 
    public void run() { 
     System.out.println("THREAD_2_START"); 
     System.out.println("GET_A_BY_THREAD_2:"+counter.getA(2)); 
     System.out.println("THREAD_2_END"); 

    } 
}); 
    threadA.start(); 
    threadB.start(); 
} 
} 

public class Counter{ 
String A = "NONE"; 

public void add(long value){ 
    synchronized (A) { 
     System.out.println("LOCKED_BY_"+value); 

     for(int i = 0; i < 1000000000; i++){} 

     setA("THREAD_"+value); 
     System.out.println("GET_A_BY_THREAD:"+getA(value)); 
    } 
} 

public void setA(String A){ 
     System.out.println("Counter.setA()"); 
     this.A = A; 
     System.out.println("Counter.setA()_end"); 
} 

public String getA(long value){ 
    System.out.println("Counter.getA()_BY_"+value); 
    return this.A; 
} 
} 

的输出是:

THREAD_1_START 
THREAD_2_START 
LOCKED_BY_1 
Counter.getA()_BY_2 
GET_A_BY_THREAD_2:NONE 
THREAD_2_END 
Counter.setA() 
Counter.setA()_end 
Counter.getA()_BY_1 
GET_A_BY_THREAD:THREAD_1 
THREAD_1_END 

Thread_1锁定 “A” 的字符串对象,并改变它,但Thread_2可以读取的值在改变之前。当“A”处于锁定状态时,thread_2如何访问“A”对象?

+0

除了已经给出的答案,您可以将'String a'标记为'final',以确保同步对象保持不变。 –

回答

4

Thread_1锁定 “A” 字符串对象,并改变它

没有, “NONE” 字符串对象Thread_1锁,创建一个新的String对象,并使用一个参考覆盖A场这个新对象。 Thread_2现在可以获得其自由锁定,但是在您当前的代码中,getA方法甚至不尝试获取它。

您必须使用锁定全部访问,而不仅仅是写入。因此getA也必须包含一个同步块。

一般规则是从不使用可变实例字段进行锁定。这样做并没有为您提供有用的保证。因此,您的A字段应该是final,您必须删除所有与此不相符的代码。

+0

如果我删除“setA(”THREAD _“+ value);”line code the code? – stackalreadyoverflowed

+0

也许这个问题稍后再编辑。但是Thread_1只设置一次A.经过由空循环引起的延迟。 –

+0

@JornVernee你说得对,我后来把这句话加到了我的答案中,现在我解开了。循环可能需要很短的时间,因为只要它被JIT编译,整个循环就会被当作死代码。 –

1

你的代码和你的解释有很多错误。

  1. 同步块只同步使用相同监视器的块(在您的情况下为A)。

    由于您的getter未同步,因此不受锁的影响。 它不会被阻塞,可能在第一个线程持有锁的时候运行,即使它在第一个线程释放它之后执行锁,它可能看不到第一个线程完成的更改。

    将同步添加到getter以解决此问题。

  2. 您锁定A"NONE",但是然后将A更改为指向不同的字符串。接下来的字符串会看到(实际上可能会看到)新引用,并锁定新的字符串,因此您没有两个锁定同一对象的同步块。

    您几乎总是希望锁定包含同步块的实例或其类上的实例。这正是您使用同步(静态)方法而不是同步块时所获得的结果。

  3. 你的措辞,而且是很重要的:

    您锁定对象A和改变计数器的实例。

    您无法锁定对象,也无法更改字符串。您只能将引用更改为指向新的字符串。