Regarding the question in the 1st topic: ( https://shipilev.net/jvm-anatomy-park/1-lock-coarsening-for-... ) " for (...) { synchronized (obj) { // something } } …could it optimize into this? synchronized (this) { for (...) { The answer would be no in general, I think, since it is unsafe. Moving the entire loop into the synchronized block would make the statement marked XXX above, i.e. "for (...)" execute inside the…
JVM Anatomy Park
21–30 of 33 posts
Re: JVM Anatomy Park
#22This is of no real consequence except curiosity, but I'm at a loss to understand what "park" means in the title. I don't even know what sense of the word is intended. Is it "park" like a city or amusement park that you visit, and the idea is that you enjoy a variety of different activities while you're there? Is it "park" like a ballpark, and the analogy is that sporting events take place there and each one of these…
Maybe it is a reference to the Rick and Morty episode "Anatomy Park". http://rickandmorty.wikia.com/wiki/Anatomy_Park_(episode)
Re: JVM Anatomy Park
#23Regarding the question in the 1st topic: ( https://shipilev.net/jvm-anatomy-park/1-lock-coarsening-for-... ) " for (...) { synchronized (obj) { // something } } …could it optimize into this? synchronized (this) { for (...) { The answer would be no in general, I think, since it is unsafe. Moving the entire loop into the synchronized block would make the statement marked XXX above, i.e. "for (...)" execute inside the…
Neither of those optimizations looks safe to me. Couldn't they potentially introduce deadlocks?
Consider the unoptimized version. It's possible that after you release the lock, another thread will take it. But I'm pretty confident there is no guarantee of this, and your own thread might immediately take the lock back. (Probably often, especially if there's just one CPU.) If taking the lock back immediately causes a deadlock, then your code is already broken as written.
So you already must account for the possibility that nothing changes between the time you release the lock and take it again. All the optimization does is take away the _other_ possibility, which was something you couldn't rely on anyway.
Re: JVM Anatomy Park
#24Earlier quoted context omitted.
Neither of those optimizations looks safe to me. Couldn't they potentially introduce deadlocks?
I had the same reaction, but now I'm starting to suspect it actually is safe, for a reason that's not immediately obvious. Consider the unoptimized version. It's possible that after you release the lock, another thread will take it. But I'm pretty confident there is no guarantee of this, and your own thread might immediately take the lock back. (Probably often, especially if there's just one CPU.) If taking the lock…
The set of possible executions of a correctly optimized program should be a subset of---and specifically, need not be necessarily equal to---the possible executions of the original unoptimized version.
Re: JVM Anatomy Park
#25Regarding the question in the 1st topic: ( https://shipilev.net/jvm-anatomy-park/1-lock-coarsening-for-... ) " for (...) { synchronized (obj) { // something } } …could it optimize into this? synchronized (this) { for (...) { The answer would be no in general, I think, since it is unsafe. Moving the entire loop into the synchronized block would make the statement marked XXX above, i.e. "for (...)" execute inside the…
> Moving the entire loop into the synchronized block would make the statement marked XXX above, i.e. "for (...)" execute inside the synchronized block, which has the potential to change the semantics (for example, the statement may include an rpc, and we don't want to make that rpc under the lock). That's not change in semantics. The JVM doesn't provide any guarantees about parallelism or scheduling semantics in the…
Let me try to answer with an example. Let us say, we have original code like this:
synchronized(this) {
a();
b();
}
c();
synchronized(this) {
d();
e();
}
It would be unsafe (in general) to transform the above code to synchronized(this) {
a();
b();
c();
d();
e();
}
Simply because Compiler does not know (again, in general) what may happen during the execution of c(). However, the following transformation is safe (the timing behavior changes, but as you point out, that is not a guarantee programmers should expect). synchronized(this) {
p();
q();
}
synchronized(this) {
r();
s();
}
to synchronized(this) {
p();
q();
r();
s();
}
since there is nothing happening between the two synchronized sections.For the for loop, an example of code where pulling the synchronized statement out of the loop is problematic:
for(a = AcquireLock(), c = 0; c
I don't think it is safe to transform to a = AcquireLock();
synchronized(this) {
for(c = 0; c
Perhaps you (and the original blog post) assume we are only talking about movement of code after ensuring that such movement is safe, but it was not clear from the document.Re: JVM Anatomy Park
#26Earlier quoted context omitted.
> Moving the entire loop into the synchronized block would make the statement marked XXX above, i.e. "for (...)" execute inside the synchronized block, which has the potential to change the semantics (for example, the statement may include an rpc, and we don't want to make that rpc under the lock). That's not change in semantics. The JVM doesn't provide any guarantees about parallelism or scheduling semantics in the…
>And it's unsafe? For what definition of safety? Let me try to answer with an example. Let us say, we have original code like this: synchronized(this) { a(); b(); } c(); synchronized(this) { d(); e(); } It would be unsafe (in general) to transform the above code to synchronized(this) { a(); b(); c(); d(); e(); } Simply because Compiler does not know (again, in general) what may happen during the execution of c(). How…
Re: JVM Anatomy Park
#27Earlier quoted context omitted.
>And it's unsafe? For what definition of safety? Let me try to answer with an example. Let us say, we have original code like this: synchronized(this) { a(); b(); } c(); synchronized(this) { d(); e(); } It would be unsafe (in general) to transform the above code to synchronized(this) { a(); b(); c(); d(); e(); } Simply because Compiler does not know (again, in general) what may happen during the execution of c(). How…
Just looking at your final example, I can't understand how you can say that is unsafe. How would anyone be able to tell the difference between the two? I think anything you tell me I'm just going to be able to answer 'but Java never guaranteed you that in the first place'. If nobody can tell the difference then how can it be unsafe?
Let me change the example a bit. Say we have two locks aL and bL, that we must always acquire in the order aL first and then bL.
Following the rule, say we write code like this:
import java.util.concurrent.locks.ReentrantLock;
class X {
private static ReentrantLock aL = new ReentrantLock();
private static ReentrantLock bL = new ReentrantLock();
static int x = 0;
static int c = 0;
static public void main(String[] args) {
for(aL.lock(); c
If I understood it right, the blog post was asking a question whether JVM can transform this to: import java.util.concurrent.locks.ReentrantLock;
class X {
private static ReentrantLock aL = new ReentrantLock();
private static ReentrantLock bL = new ReentrantLock();
static int x = 0;
static int c = 0;
static public void main(String[] args) {
synchronized(bL) {
for(aL.lock(); c
Since the locks are now acquired in a different order, does that not qualify as observable behavior?Re: JVM Anatomy Park
#28Earlier quoted context omitted.
Just looking at your final example, I can't understand how you can say that is unsafe. How would anyone be able to tell the difference between the two? I think anything you tell me I'm just going to be able to answer 'but Java never guaranteed you that in the first place'. If nobody can tell the difference then how can it be unsafe?
>Just looking at your final example, I can't understand how you can say that is unsafe Let me change the example a bit. Say we have two locks aL and bL, that we must always acquire in the order aL first and then bL. Following the rule, say we write code like this: import java.util.concurrent.locks.ReentrantLock; class X { private static ReentrantLock aL = new ReentrantLock(); private static ReentrantLock bL = new Ree…
Re: JVM Anatomy Park
#29Earlier quoted context omitted.
>Just looking at your final example, I can't understand how you can say that is unsafe Let me change the example a bit. Say we have two locks aL and bL, that we must always acquire in the order aL first and then bL. Following the rule, say we write code like this: import java.util.concurrent.locks.ReentrantLock; class X { private static ReentrantLock aL = new ReentrantLock(); private static ReentrantLock bL = new Ree…
But that's just a different example to the one you gave before. In your previous example acquiring the explicit lock always came before the start of synchronised block, both before and after the rewrite. You've changed it here so it's a different question.
synchronized(this) {
a = AcquireLock();
for(c = 0; c
which is inline with what the blog post was proposing.To repeat the blog is a question:
for (...) {
synchronized (obj) {
// something
}
}
…could it optimize into this? synchronized (this) {
for (...) {
// something
}
}
My answer to that is in general, no.Re: JVM Anatomy Park
#30Earlier quoted context omitted.
But that's just a different example to the one you gave before. In your previous example acquiring the explicit lock always came before the start of synchronised block, both before and after the rewrite. You've changed it here so it's a different question.
Sorry, I meant to write: synchronized(this) { a = AcquireLock(); for(c = 0; c which is inline with what the blog post was proposing. To repeat the blog is a question: for (...) { synchronized (obj) { // something } } …could it optimize into this? synchronized (this) { for (...) { // something } } My answer to that is in general, no.
Yes, but I think it's an assumption so obvious as to be not worth stating that the author means as long as ... does not do that.