简体   繁体   English

Java中的对象锁定

[英]Object lock in Java

I am trying to understand "synchronized block" in Java. 我试图理解Java中的“同步块”。 I have written very basic code to see what happens if I lock and change the object in thread_1 and access to it from another thread_2 (race condition) via another method. 我编写了非常基本的代码,以了解如果锁定并更改线程_1中的对象并通过另一个方法从另一个线程_2(竞争条件)访问该对象会发生什么。 But I am in trouble to understand the behaviour because I was expecting Thread_1 would change value first and then Thread_2 would access the new value but the result was not as I expected. 但是我很难理解该行为,因为我期望Thread_1首先更改值,然后Thread_2将访问新值,但结果与我预期的不同。

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;
}
}

The Output is: 输出为:

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 locks the "A" string object and changes it but Thread_2 can read the value before it changes. 线程_1锁定“ A”字符串对象并对其进行更改,但是线程_2可以在更改之前读取该值。 When "A" is in lock, how can thread_2 can access the "A" object? 当“ A”处于锁定状态时,thread_2如何访问“ A”对象?

Thread_1 locks the "A" string object and changes it Thread_1锁定“ A”字符串对象并对其进行更改

No, Thread_1 locks on the "NONE" string object, creates a new String object, and overwrites the A field with a reference to this new object. 否,Thread_1锁定“ NONE”字符串对象,创建一个新的String对象,并使用对该新对象的引用覆盖A字段。 Thread_2 can now acquire its free lock, however in your current code the getA method doesn't even attempt to acquire it. Thread_2现在可以获取其自由锁,但是在您当前的代码中, getA方法甚至没有尝试获取它。

You must use locking for all access, not just writing. 您必须对所有访问使用锁定,而不仅仅是写。 Therefore getA must also contain a synchronized block. 因此, getA还必须包含一个同步块。

The general rule is to never use mutable instance fields for locking. 一般规则是永远不要使用可变实例字段进行锁定。 Doing that provides you with no useful guarantees. 这样做不能为您提供有用的保证。 Therefore your A field should be final and you must delete all code that disagrees with that. 因此,您的A字段应为final字段,并且您必须删除所有与此不同的代码。

There are multiple things wrong with your code and your interpretation of it. 您的代码及其解释有很多问题。

  1. The synchronize block synchronizes only blocks using the same monitor ( A in your case). 同步块仅使用同一监视器(在您的情况下为A )同步块。

    Since your getter is not synchronized it is not affected by the lock. 由于您的getter未同步,因此不会受到锁定的影响。 It doesn't get blocked, might run during the first thread is holding the lock and even when it executes after the first thread released it's lock it might not see changes done by the first thread. 它不会被阻塞,可能在第一个线程持有该锁的过程中运行,即使在第一个线程释放它的锁之后执行,它也可能看不到第一个线程所做的更改。

    Add synchronization to the getter to fix this issue. 向getter添加同步以解决此问题。

  2. You lock on A ie on "NONE" , but then change A to point to a different String. 您将A锁定在"NONE" ,但随后将A更改为指向另一个String。 The next String comming along sees (actually might see) the new reference, and locks on the new String so again you have no two synchronized blocks that lock on the same object. 接下来的String看到(实际上可能会看到)新引用,并锁定在新的String上,因此您再也没有两个锁定在同一对象上的同步块。

    You almost always want to lock on the instance that contains the synchronized block or on it's class. 您几乎总是想锁定包含同步块或其类的实例。 This is exactly what you get when you use synchronized (static) methods instead of synchronized blocks. 这正是使用同步(静态)方法而不是同步块时得到的。

  3. Your wording is of, and it is important: 您的用语是,而且很重要:

    You lock on the object A and change the instance of Counter. 您锁定对象A并更改Counter的实例。

    You cannot lock an object and you cannot change a String. 您不能锁定对象,也不能更改字符串。 You can only change References to point to a new String. 您只能将引用更改为指向新的字符串。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM