Java中的对象锁

时间:2016-09-06 11:39:50

标签: java synchronization synchronized synchronized-block

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

<input type="radio" ng-model="radioValue" ng-value="showText">
<input type="radio" ng-model="radioValue" ng-value="HideText">

输出是:

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锁定“A”字符串对象并更改它,但Thread_2可以在更改之前读取该值。当“A”处于锁定状态时,thread_2如何访问“A”对象?

2 个答案:

答案 0 :(得分:4)

  

Thread_1锁定&#34; A&#34;字符串对象并对其进行更改

不,Thread_1锁定&#34; NONE&#34; string对象,创建一个新的String对象,并使用对这个新对象的引用覆盖A字段。 Thread_2现在可以获得其免费锁定,但是在您当前的代码中,getA方法甚至不会尝试获取它。

您必须使用锁定来进行所有访问,而不仅仅是写入。因此getA还必须包含同步块。

一般规则是永远不要使用可变实例字段进行锁定。这样做不会为您提供有用的保证。因此,您的A字段应为final,并且您必须删除所有不同意的代码。

答案 1 :(得分:1)

您的代码及其解释存在多种问题。

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

    由于您的getter未同步,因此不会受到锁定的影响。 它没有被阻止,可能在第一个线程持有锁的过程中运行,甚至在第一个线程释放它之后执行锁定它可能看不到第一个线程所做的更改。

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

  2. 您锁定A,即"NONE",但随后更改A以指向其他字符串。接下来的String会看到(实际上可能会看到)新引用,并锁定新的String,所以再没有两个同步块锁定同一个对象。

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

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

    您锁定对象A并更改计数器的实例。

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