Java synchronized instance member doesn't work in a nested way












3














When programming with Java synchronized, I happend to find a usage which doesn't work as I expect. That is, in thread A, it accesses instance member (content) inside two nested synchronized blocks:



synchronized(this){
synchronized (content) {content = str;}
}


while in thread B, it accesses the same content in only one synchronized block:



synchronized (content) {content = str;}


I expected this can work as a normal usage of



synchronized (content) {content = str;}


however, it doesn't. It works as there is no synchronized.
The complete code and logs are as following:



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


Logs:



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


Why doesn't this happen?










share|improve this question




















  • 2




    It's not clear what you're asking. Is there a problem? What's the expected result? What's the actual result?
    – Charlie
    Nov 13 '18 at 6:36






  • 1




    consider synchronized (content) { content = str; // Strings are immutatable - what you are locking on has changed
    – Scary Wombat
    Nov 13 '18 at 6:40










  • Probably because none of those locks are common. You are synchronizing on different objects. Each time you create a SynConcent you create a new string. That won't work.
    – markspace
    Nov 13 '18 at 6:40










  • @Charlie, it's expected to synchrolize the instance member content.
    – Michael Wang
    Nov 13 '18 at 9:43










  • @ScaryWombat Correct.
    – Michael Wang
    Nov 13 '18 at 9:45
















3














When programming with Java synchronized, I happend to find a usage which doesn't work as I expect. That is, in thread A, it accesses instance member (content) inside two nested synchronized blocks:



synchronized(this){
synchronized (content) {content = str;}
}


while in thread B, it accesses the same content in only one synchronized block:



synchronized (content) {content = str;}


I expected this can work as a normal usage of



synchronized (content) {content = str;}


however, it doesn't. It works as there is no synchronized.
The complete code and logs are as following:



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


Logs:



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


Why doesn't this happen?










share|improve this question




















  • 2




    It's not clear what you're asking. Is there a problem? What's the expected result? What's the actual result?
    – Charlie
    Nov 13 '18 at 6:36






  • 1




    consider synchronized (content) { content = str; // Strings are immutatable - what you are locking on has changed
    – Scary Wombat
    Nov 13 '18 at 6:40










  • Probably because none of those locks are common. You are synchronizing on different objects. Each time you create a SynConcent you create a new string. That won't work.
    – markspace
    Nov 13 '18 at 6:40










  • @Charlie, it's expected to synchrolize the instance member content.
    – Michael Wang
    Nov 13 '18 at 9:43










  • @ScaryWombat Correct.
    – Michael Wang
    Nov 13 '18 at 9:45














3












3








3







When programming with Java synchronized, I happend to find a usage which doesn't work as I expect. That is, in thread A, it accesses instance member (content) inside two nested synchronized blocks:



synchronized(this){
synchronized (content) {content = str;}
}


while in thread B, it accesses the same content in only one synchronized block:



synchronized (content) {content = str;}


I expected this can work as a normal usage of



synchronized (content) {content = str;}


however, it doesn't. It works as there is no synchronized.
The complete code and logs are as following:



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


Logs:



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


Why doesn't this happen?










share|improve this question















When programming with Java synchronized, I happend to find a usage which doesn't work as I expect. That is, in thread A, it accesses instance member (content) inside two nested synchronized blocks:



synchronized(this){
synchronized (content) {content = str;}
}


while in thread B, it accesses the same content in only one synchronized block:



synchronized (content) {content = str;}


I expected this can work as a normal usage of



synchronized (content) {content = str;}


however, it doesn't. It works as there is no synchronized.
The complete code and logs are as following:



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


Logs:



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


Why doesn't this happen?







java multithreading synchronized






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 13 '18 at 6:53









Mureinik

180k22130199




180k22130199










asked Nov 13 '18 at 6:30









Michael Wang

183




183








  • 2




    It's not clear what you're asking. Is there a problem? What's the expected result? What's the actual result?
    – Charlie
    Nov 13 '18 at 6:36






  • 1




    consider synchronized (content) { content = str; // Strings are immutatable - what you are locking on has changed
    – Scary Wombat
    Nov 13 '18 at 6:40










  • Probably because none of those locks are common. You are synchronizing on different objects. Each time you create a SynConcent you create a new string. That won't work.
    – markspace
    Nov 13 '18 at 6:40










  • @Charlie, it's expected to synchrolize the instance member content.
    – Michael Wang
    Nov 13 '18 at 9:43










  • @ScaryWombat Correct.
    – Michael Wang
    Nov 13 '18 at 9:45














  • 2




    It's not clear what you're asking. Is there a problem? What's the expected result? What's the actual result?
    – Charlie
    Nov 13 '18 at 6:36






  • 1




    consider synchronized (content) { content = str; // Strings are immutatable - what you are locking on has changed
    – Scary Wombat
    Nov 13 '18 at 6:40










  • Probably because none of those locks are common. You are synchronizing on different objects. Each time you create a SynConcent you create a new string. That won't work.
    – markspace
    Nov 13 '18 at 6:40










  • @Charlie, it's expected to synchrolize the instance member content.
    – Michael Wang
    Nov 13 '18 at 9:43










  • @ScaryWombat Correct.
    – Michael Wang
    Nov 13 '18 at 9:45








2




2




It's not clear what you're asking. Is there a problem? What's the expected result? What's the actual result?
– Charlie
Nov 13 '18 at 6:36




It's not clear what you're asking. Is there a problem? What's the expected result? What's the actual result?
– Charlie
Nov 13 '18 at 6:36




1




1




consider synchronized (content) { content = str; // Strings are immutatable - what you are locking on has changed
– Scary Wombat
Nov 13 '18 at 6:40




consider synchronized (content) { content = str; // Strings are immutatable - what you are locking on has changed
– Scary Wombat
Nov 13 '18 at 6:40












Probably because none of those locks are common. You are synchronizing on different objects. Each time you create a SynConcent you create a new string. That won't work.
– markspace
Nov 13 '18 at 6:40




Probably because none of those locks are common. You are synchronizing on different objects. Each time you create a SynConcent you create a new string. That won't work.
– markspace
Nov 13 '18 at 6:40












@Charlie, it's expected to synchrolize the instance member content.
– Michael Wang
Nov 13 '18 at 9:43




@Charlie, it's expected to synchrolize the instance member content.
– Michael Wang
Nov 13 '18 at 9:43












@ScaryWombat Correct.
– Michael Wang
Nov 13 '18 at 9:45




@ScaryWombat Correct.
– Michael Wang
Nov 13 '18 at 9:45












1 Answer
1






active

oldest

votes


















5














synchronized applies to an object, not a name of a variable. You enter the synchronized block by locking content, but then in the block, you assign a different object to it. The next time you try to synchronize content you'll be synchronizing different object, so the previously held lock won't interfere with it.



If you use a mutable object, such as a StringBuilder and change its data instead, you'll see the behavior you expect:



synchronized (content) {
content.setLength(0); // Remove the old content
content.append(str); // Set the new content
// Rest of the code...





share|improve this answer





















  • That's right. I modified the example code and it works correctly now.
    – Michael Wang
    Nov 13 '18 at 9:41










  • This is really a good lesson, as it often uses immutatable variables, e.g. string.
    – Michael Wang
    Nov 13 '18 at 9:59








  • 2




    It might be worth pointing out that this mistake can be avoided by always following a simple rule: If you write synchronized (foo) { ... } then foo should be a final variable. If the compiler won't let you do that, then you're either doing something very tricky, or else you're making a mistake. And since "tricky" does not usually mean "good", it's usually best to just follow the rule.
    – Solomon Slow
    Nov 13 '18 at 14:53












  • @SolomonSlow Good, a simple but useful rule
    – Michael Wang
    Nov 14 '18 at 1:32










  • BTW, another solution is, to add a flag (e.g. an integer) and synchrolize it wherever accessing these immutable variables, as changing these immutable variables to mutable ones might be annoying or impossible sometimes.
    – Michael Wang
    Nov 15 '18 at 2:05











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53275060%2fjava-synchronized-instance-member-doesnt-work-in-a-nested-way%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









5














synchronized applies to an object, not a name of a variable. You enter the synchronized block by locking content, but then in the block, you assign a different object to it. The next time you try to synchronize content you'll be synchronizing different object, so the previously held lock won't interfere with it.



If you use a mutable object, such as a StringBuilder and change its data instead, you'll see the behavior you expect:



synchronized (content) {
content.setLength(0); // Remove the old content
content.append(str); // Set the new content
// Rest of the code...





share|improve this answer





















  • That's right. I modified the example code and it works correctly now.
    – Michael Wang
    Nov 13 '18 at 9:41










  • This is really a good lesson, as it often uses immutatable variables, e.g. string.
    – Michael Wang
    Nov 13 '18 at 9:59








  • 2




    It might be worth pointing out that this mistake can be avoided by always following a simple rule: If you write synchronized (foo) { ... } then foo should be a final variable. If the compiler won't let you do that, then you're either doing something very tricky, or else you're making a mistake. And since "tricky" does not usually mean "good", it's usually best to just follow the rule.
    – Solomon Slow
    Nov 13 '18 at 14:53












  • @SolomonSlow Good, a simple but useful rule
    – Michael Wang
    Nov 14 '18 at 1:32










  • BTW, another solution is, to add a flag (e.g. an integer) and synchrolize it wherever accessing these immutable variables, as changing these immutable variables to mutable ones might be annoying or impossible sometimes.
    – Michael Wang
    Nov 15 '18 at 2:05
















5














synchronized applies to an object, not a name of a variable. You enter the synchronized block by locking content, but then in the block, you assign a different object to it. The next time you try to synchronize content you'll be synchronizing different object, so the previously held lock won't interfere with it.



If you use a mutable object, such as a StringBuilder and change its data instead, you'll see the behavior you expect:



synchronized (content) {
content.setLength(0); // Remove the old content
content.append(str); // Set the new content
// Rest of the code...





share|improve this answer





















  • That's right. I modified the example code and it works correctly now.
    – Michael Wang
    Nov 13 '18 at 9:41










  • This is really a good lesson, as it often uses immutatable variables, e.g. string.
    – Michael Wang
    Nov 13 '18 at 9:59








  • 2




    It might be worth pointing out that this mistake can be avoided by always following a simple rule: If you write synchronized (foo) { ... } then foo should be a final variable. If the compiler won't let you do that, then you're either doing something very tricky, or else you're making a mistake. And since "tricky" does not usually mean "good", it's usually best to just follow the rule.
    – Solomon Slow
    Nov 13 '18 at 14:53












  • @SolomonSlow Good, a simple but useful rule
    – Michael Wang
    Nov 14 '18 at 1:32










  • BTW, another solution is, to add a flag (e.g. an integer) and synchrolize it wherever accessing these immutable variables, as changing these immutable variables to mutable ones might be annoying or impossible sometimes.
    – Michael Wang
    Nov 15 '18 at 2:05














5












5








5






synchronized applies to an object, not a name of a variable. You enter the synchronized block by locking content, but then in the block, you assign a different object to it. The next time you try to synchronize content you'll be synchronizing different object, so the previously held lock won't interfere with it.



If you use a mutable object, such as a StringBuilder and change its data instead, you'll see the behavior you expect:



synchronized (content) {
content.setLength(0); // Remove the old content
content.append(str); // Set the new content
// Rest of the code...





share|improve this answer












synchronized applies to an object, not a name of a variable. You enter the synchronized block by locking content, but then in the block, you assign a different object to it. The next time you try to synchronize content you'll be synchronizing different object, so the previously held lock won't interfere with it.



If you use a mutable object, such as a StringBuilder and change its data instead, you'll see the behavior you expect:



synchronized (content) {
content.setLength(0); // Remove the old content
content.append(str); // Set the new content
// Rest of the code...






share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 13 '18 at 6:49









Mureinik

180k22130199




180k22130199












  • That's right. I modified the example code and it works correctly now.
    – Michael Wang
    Nov 13 '18 at 9:41










  • This is really a good lesson, as it often uses immutatable variables, e.g. string.
    – Michael Wang
    Nov 13 '18 at 9:59








  • 2




    It might be worth pointing out that this mistake can be avoided by always following a simple rule: If you write synchronized (foo) { ... } then foo should be a final variable. If the compiler won't let you do that, then you're either doing something very tricky, or else you're making a mistake. And since "tricky" does not usually mean "good", it's usually best to just follow the rule.
    – Solomon Slow
    Nov 13 '18 at 14:53












  • @SolomonSlow Good, a simple but useful rule
    – Michael Wang
    Nov 14 '18 at 1:32










  • BTW, another solution is, to add a flag (e.g. an integer) and synchrolize it wherever accessing these immutable variables, as changing these immutable variables to mutable ones might be annoying or impossible sometimes.
    – Michael Wang
    Nov 15 '18 at 2:05


















  • That's right. I modified the example code and it works correctly now.
    – Michael Wang
    Nov 13 '18 at 9:41










  • This is really a good lesson, as it often uses immutatable variables, e.g. string.
    – Michael Wang
    Nov 13 '18 at 9:59








  • 2




    It might be worth pointing out that this mistake can be avoided by always following a simple rule: If you write synchronized (foo) { ... } then foo should be a final variable. If the compiler won't let you do that, then you're either doing something very tricky, or else you're making a mistake. And since "tricky" does not usually mean "good", it's usually best to just follow the rule.
    – Solomon Slow
    Nov 13 '18 at 14:53












  • @SolomonSlow Good, a simple but useful rule
    – Michael Wang
    Nov 14 '18 at 1:32










  • BTW, another solution is, to add a flag (e.g. an integer) and synchrolize it wherever accessing these immutable variables, as changing these immutable variables to mutable ones might be annoying or impossible sometimes.
    – Michael Wang
    Nov 15 '18 at 2:05
















That's right. I modified the example code and it works correctly now.
– Michael Wang
Nov 13 '18 at 9:41




That's right. I modified the example code and it works correctly now.
– Michael Wang
Nov 13 '18 at 9:41












This is really a good lesson, as it often uses immutatable variables, e.g. string.
– Michael Wang
Nov 13 '18 at 9:59






This is really a good lesson, as it often uses immutatable variables, e.g. string.
– Michael Wang
Nov 13 '18 at 9:59






2




2




It might be worth pointing out that this mistake can be avoided by always following a simple rule: If you write synchronized (foo) { ... } then foo should be a final variable. If the compiler won't let you do that, then you're either doing something very tricky, or else you're making a mistake. And since "tricky" does not usually mean "good", it's usually best to just follow the rule.
– Solomon Slow
Nov 13 '18 at 14:53






It might be worth pointing out that this mistake can be avoided by always following a simple rule: If you write synchronized (foo) { ... } then foo should be a final variable. If the compiler won't let you do that, then you're either doing something very tricky, or else you're making a mistake. And since "tricky" does not usually mean "good", it's usually best to just follow the rule.
– Solomon Slow
Nov 13 '18 at 14:53














@SolomonSlow Good, a simple but useful rule
– Michael Wang
Nov 14 '18 at 1:32




@SolomonSlow Good, a simple but useful rule
– Michael Wang
Nov 14 '18 at 1:32












BTW, another solution is, to add a flag (e.g. an integer) and synchrolize it wherever accessing these immutable variables, as changing these immutable variables to mutable ones might be annoying or impossible sometimes.
– Michael Wang
Nov 15 '18 at 2:05




BTW, another solution is, to add a flag (e.g. an integer) and synchrolize it wherever accessing these immutable variables, as changing these immutable variables to mutable ones might be annoying or impossible sometimes.
– Michael Wang
Nov 15 '18 at 2:05


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53275060%2fjava-synchronized-instance-member-doesnt-work-in-a-nested-way%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Bressuire

Vorschmack

Quarantine