Java 同步实例成员不能以嵌套方式工作
Java synchronized instance member doesn't work in a nested way
在使用Java synchronized 进行编程时,我碰巧发现了一种用法,但并不像我预期的那样有效。也就是说,在线程 A 中,它访问两个嵌套同步块内的实例成员(内容):
synchronized(this){
synchronized (content) {content = str;}
}
而在线程 B 中,它仅在一个同步块中访问相同的内容:
synchronized (content) {content = str;}
我希望这可以正常使用
synchronized (content) {content = str;}
然而,事实并非如此。它可以工作,因为没有同步。
完整代码和日志如下:
public class JavaSync {
public static void main(String[] args) {
SyncContent syncContent = new SyncContent("JavaSync");
ThreadA a = new ThreadA(syncContent);
a.setName("A");
a.start();
ThreadB b = new ThreadB(syncContent);
b.setName("B");
b.start();
ThreadC c = new ThreadC(syncContent);
c.setName("C");
c.start();
}
}
class SyncContent {
volatile String content = new String();
public SyncContent(String content) {
this.content = content;
}
private double timeConsuming() {
double a, b, c;
double sum = 0;
for (int i = 1; i < 2000000; i++) {
a = i + sum / ( i * 19);
b = a / 17;
c = b * 23;
sum += (b + c - a) / (a + i);
}
return sum;
}
synchronized public void syncFunc(String str) {
System.out.println("syncFunc.Thread: " + Thread.currentThread().getName() + " enter: " + System.currentTimeMillis());
synchronized (content) {
System.out.println("syncFunc.Thread: " + Thread.currentThread().getName() + " content old: " + content);
content = str;
System.out.println("syncFunc.Thread: " + Thread.currentThread().getName() + " content new: " + content);
//Thread.sleep(2000); // InterruptedException
System.out.println("syncFunc.Thread: dummy result: " + timeConsuming());
System.out.println("syncFunc.Thread: " + Thread.currentThread().getName() + " content final: " + content);
}
System.out.println("syncFunc.Thread: " + Thread.currentThread().getName() + " exit: " + System.currentTimeMillis());
/*try {
} catch (Exception e) {
e.printStackTrace();
}*/
}
public void syncThis(String str) {
synchronized(this) {
System.out.println("syncThis.Thread: " + Thread.currentThread().getName() + " enter: " + System.currentTimeMillis());
synchronized (content) {
System.out.println("syncThis.Thread: " + Thread.currentThread().getName() + " content old: " + content);
content = str;
System.out.println("syncThis.Thread: " + Thread.currentThread().getName() + " content new: " + content);
//Thread.sleep(2000); // InterruptedException
System.out.println("syncThis.Thread: dummy result: " + timeConsuming());
System.out.println("syncThis.Thread: " + Thread.currentThread().getName() + " content final: " + content);
}
System.out.println("syncThis.Thread: " + Thread.currentThread().getName() + " exit: " + System.currentTimeMillis());
/*try {
} catch (Exception e) {
e.printStackTrace();
}*/
}
}
public void syncVariable(String str) {
synchronized(content) {
System.out.println("syncVariable.Thread: " + Thread.currentThread().getName() + " enter: " + System.currentTimeMillis());
System.out.println("syncVariable.Thread: " + Thread.currentThread().getName() + " content old: " + content);
content = str;
System.out.println("syncVariable.Thread: " + Thread.currentThread().getName() + " content new: " + content);
System.out.println("syncVariable.Thread: " + Thread.currentThread().getName() + " exit: " + System.currentTimeMillis());
}
}
}
class ThreadA extends Thread {
private SyncContent syncContent;
private String me = "ThreadA";
public ThreadA(SyncContent syncContent) {
super();
this.syncContent = syncContent;
}
@Override
public void run() {
syncContent.syncThis(me);
}
}
class ThreadB extends Thread {
private SyncContent syncContent;
private String me = "ThreadB";
public ThreadB(SyncContent syncContent) {
super();
this.syncContent = syncContent;
}
@Override
public void run() {
syncContent.syncFunc(me);
}
}
class ThreadC extends Thread {
private SyncContent syncContent;
private String me = "ThreadC";
public ThreadC(SyncContent syncContent) {
super();
this.syncContent = syncContent;
}
@Override
public void run() {
syncContent.syncVariable(me);
}
}
日志:
syncThis.Thread: A enter: 1542076529822
syncThis.Thread: A content old: JavaSync
syncThis.Thread: A content new: ThreadA
syncVariable.Thread: C enter: 1542076529823
syncVariable.Thread: C content old: ThreadA
syncVariable.Thread: C content new: ThreadC
syncVariable.Thread: C exit: 1542076529824
syncThis.Thread: dummy result: 411764.5149938948
syncThis.Thread: A content final: ThreadC
syncThis.Thread: A exit: 1542076529862
syncFunc.Thread: B enter: 1542076529862
syncFunc.Thread: B content old: ThreadC
syncFunc.Thread: B content new: ThreadB
syncFunc.Thread: dummy result: 411764.5149938948
syncFunc.Thread: B content final: ThreadB
syncFunc.Thread: B exit: 1542076529897
为什么没有发生这种情况?
synchronized
适用于对象,而不是变量名。您通过锁定 content
进入 synchronized
块,但随后在该块中,您为其分配了一个不同的对象。下次您尝试同步 content
时,您将同步 不同的对象 ,因此之前持有的锁不会干扰它。
如果您使用可变对象,例如 StringBuilder
并改为更改其数据,您将看到您期望的行为:
synchronized (content) {
content.setLength(0); // Remove the old content
content.append(str); // Set the new content
// Rest of the code...
在使用Java synchronized 进行编程时,我碰巧发现了一种用法,但并不像我预期的那样有效。也就是说,在线程 A 中,它访问两个嵌套同步块内的实例成员(内容):
synchronized(this){
synchronized (content) {content = str;}
}
而在线程 B 中,它仅在一个同步块中访问相同的内容:
synchronized (content) {content = str;}
我希望这可以正常使用
synchronized (content) {content = str;}
然而,事实并非如此。它可以工作,因为没有同步。 完整代码和日志如下:
public class JavaSync {
public static void main(String[] args) {
SyncContent syncContent = new SyncContent("JavaSync");
ThreadA a = new ThreadA(syncContent);
a.setName("A");
a.start();
ThreadB b = new ThreadB(syncContent);
b.setName("B");
b.start();
ThreadC c = new ThreadC(syncContent);
c.setName("C");
c.start();
}
}
class SyncContent {
volatile String content = new String();
public SyncContent(String content) {
this.content = content;
}
private double timeConsuming() {
double a, b, c;
double sum = 0;
for (int i = 1; i < 2000000; i++) {
a = i + sum / ( i * 19);
b = a / 17;
c = b * 23;
sum += (b + c - a) / (a + i);
}
return sum;
}
synchronized public void syncFunc(String str) {
System.out.println("syncFunc.Thread: " + Thread.currentThread().getName() + " enter: " + System.currentTimeMillis());
synchronized (content) {
System.out.println("syncFunc.Thread: " + Thread.currentThread().getName() + " content old: " + content);
content = str;
System.out.println("syncFunc.Thread: " + Thread.currentThread().getName() + " content new: " + content);
//Thread.sleep(2000); // InterruptedException
System.out.println("syncFunc.Thread: dummy result: " + timeConsuming());
System.out.println("syncFunc.Thread: " + Thread.currentThread().getName() + " content final: " + content);
}
System.out.println("syncFunc.Thread: " + Thread.currentThread().getName() + " exit: " + System.currentTimeMillis());
/*try {
} catch (Exception e) {
e.printStackTrace();
}*/
}
public void syncThis(String str) {
synchronized(this) {
System.out.println("syncThis.Thread: " + Thread.currentThread().getName() + " enter: " + System.currentTimeMillis());
synchronized (content) {
System.out.println("syncThis.Thread: " + Thread.currentThread().getName() + " content old: " + content);
content = str;
System.out.println("syncThis.Thread: " + Thread.currentThread().getName() + " content new: " + content);
//Thread.sleep(2000); // InterruptedException
System.out.println("syncThis.Thread: dummy result: " + timeConsuming());
System.out.println("syncThis.Thread: " + Thread.currentThread().getName() + " content final: " + content);
}
System.out.println("syncThis.Thread: " + Thread.currentThread().getName() + " exit: " + System.currentTimeMillis());
/*try {
} catch (Exception e) {
e.printStackTrace();
}*/
}
}
public void syncVariable(String str) {
synchronized(content) {
System.out.println("syncVariable.Thread: " + Thread.currentThread().getName() + " enter: " + System.currentTimeMillis());
System.out.println("syncVariable.Thread: " + Thread.currentThread().getName() + " content old: " + content);
content = str;
System.out.println("syncVariable.Thread: " + Thread.currentThread().getName() + " content new: " + content);
System.out.println("syncVariable.Thread: " + Thread.currentThread().getName() + " exit: " + System.currentTimeMillis());
}
}
}
class ThreadA extends Thread {
private SyncContent syncContent;
private String me = "ThreadA";
public ThreadA(SyncContent syncContent) {
super();
this.syncContent = syncContent;
}
@Override
public void run() {
syncContent.syncThis(me);
}
}
class ThreadB extends Thread {
private SyncContent syncContent;
private String me = "ThreadB";
public ThreadB(SyncContent syncContent) {
super();
this.syncContent = syncContent;
}
@Override
public void run() {
syncContent.syncFunc(me);
}
}
class ThreadC extends Thread {
private SyncContent syncContent;
private String me = "ThreadC";
public ThreadC(SyncContent syncContent) {
super();
this.syncContent = syncContent;
}
@Override
public void run() {
syncContent.syncVariable(me);
}
}
日志:
syncThis.Thread: A enter: 1542076529822
syncThis.Thread: A content old: JavaSync
syncThis.Thread: A content new: ThreadA
syncVariable.Thread: C enter: 1542076529823
syncVariable.Thread: C content old: ThreadA
syncVariable.Thread: C content new: ThreadC
syncVariable.Thread: C exit: 1542076529824
syncThis.Thread: dummy result: 411764.5149938948
syncThis.Thread: A content final: ThreadC
syncThis.Thread: A exit: 1542076529862
syncFunc.Thread: B enter: 1542076529862
syncFunc.Thread: B content old: ThreadC
syncFunc.Thread: B content new: ThreadB
syncFunc.Thread: dummy result: 411764.5149938948
syncFunc.Thread: B content final: ThreadB
syncFunc.Thread: B exit: 1542076529897
为什么没有发生这种情况?
synchronized
适用于对象,而不是变量名。您通过锁定 content
进入 synchronized
块,但随后在该块中,您为其分配了一个不同的对象。下次您尝试同步 content
时,您将同步 不同的对象 ,因此之前持有的锁不会干扰它。
如果您使用可变对象,例如 StringBuilder
并改为更改其数据,您将看到您期望的行为:
synchronized (content) {
content.setLength(0); // Remove the old content
content.append(str); // Set the new content
// Rest of the code...