对象锁定 Java
Object lock in Java
我正在尝试理解 Java 中的 "synchronized block"。我已经编写了非常基本的代码来查看如果我锁定并更改 thread_1 中的对象并通过另一种方法从另一个 thread_2 (竞争条件)访问它会发生什么。但是我很难理解这种行为,因为我期望 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;
}
}
输出是:
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"对象?
Thread_1 locks the "A" string object and changes it
不,Thread_1 锁定 "NONE" 字符串对象,创建一个新的 String 对象,并用对这个新对象的引用覆盖 A
字段。 Thread_2 现在可以获取它的空闲锁,但是在您当前的代码中 getA
方法甚至不会尝试获取它。
您必须对所有 访问使用锁定,而不仅仅是写入。因此 getA
也必须包含同步块。
一般规则是永远不要使用可变实例字段进行锁定。这样做不会为您提供任何有用的保证。因此,您的 A
字段应该是 final
并且您必须删除所有不同意的代码。
你的代码和你对它的解释有很多问题。
同步块仅同步使用同一监视器的块(在您的情况下为A
)。
由于您的 getter 未同步,因此不受锁定影响。
它不会被阻塞,可能 运行 在第一个线程持有锁期间,甚至当它在第一个线程释放它的锁后执行时,它可能看不到第一个线程所做的更改。
将同步添加到 getter 以解决此问题。
您锁定 A
,即 "NONE"
,然后更改 A
以指向不同的字符串。下一个 String 会看到(实际上可能会看到)新的引用,并锁定新的 String,所以你没有两个同步块锁定同一个对象。
您几乎总是想锁定包含同步块的实例或其 class。这正是您使用同步(静态)方法而不是同步块时得到的结果。
你的措辞很重要:
您锁定了对象 A 并更改了 Counter 的实例。
您不能锁定对象,也不能更改字符串。您只能更改引用以指向新的字符串。
我正在尝试理解 Java 中的 "synchronized block"。我已经编写了非常基本的代码来查看如果我锁定并更改 thread_1 中的对象并通过另一种方法从另一个 thread_2 (竞争条件)访问它会发生什么。但是我很难理解这种行为,因为我期望 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;
}
}
输出是:
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"对象?
Thread_1 locks the "A" string object and changes it
不,Thread_1 锁定 "NONE" 字符串对象,创建一个新的 String 对象,并用对这个新对象的引用覆盖 A
字段。 Thread_2 现在可以获取它的空闲锁,但是在您当前的代码中 getA
方法甚至不会尝试获取它。
您必须对所有 访问使用锁定,而不仅仅是写入。因此 getA
也必须包含同步块。
一般规则是永远不要使用可变实例字段进行锁定。这样做不会为您提供任何有用的保证。因此,您的 A
字段应该是 final
并且您必须删除所有不同意的代码。
你的代码和你对它的解释有很多问题。
同步块仅同步使用同一监视器的块(在您的情况下为
A
)。由于您的 getter 未同步,因此不受锁定影响。 它不会被阻塞,可能 运行 在第一个线程持有锁期间,甚至当它在第一个线程释放它的锁后执行时,它可能看不到第一个线程所做的更改。
将同步添加到 getter 以解决此问题。
您锁定
A
,即"NONE"
,然后更改A
以指向不同的字符串。下一个 String 会看到(实际上可能会看到)新的引用,并锁定新的 String,所以你没有两个同步块锁定同一个对象。您几乎总是想锁定包含同步块的实例或其 class。这正是您使用同步(静态)方法而不是同步块时得到的结果。
你的措辞很重要:
您锁定了对象 A 并更改了 Counter 的实例。
您不能锁定对象,也不能更改字符串。您只能更改引用以指向新的字符串。