Java Forum / General / June 2006
synchronized block in synchronized static method
dmcreyno - 26 Jun 2006 17:59 GMT I recently stumbled across this idiom.
public static synchronized int getThreadCountMax() { synchronized (ARTClientThread.class) { return threadCountMax; } }
Its a synchronized static method with a synchronized block. Looks redundant to me. Doesn't the synchronized static method use the class object?
Thomas Fritsch - 26 Jun 2006 18:14 GMT > I recently stumbled across this idiom. > [quoted text clipped - 8 lines] > Its a synchronized static method with a synchronized block. Looks > redundant to me. It is just as dumb as writing this: public static int getThreadCountMax() { synchronized (ARTClientThread.class) { synchronized (ARTClientThread.class) { return threadCountMax; } } }
> Doesn't the synchronized static method use the class > object? I does.
 Signature Thomas
Robert Klemme - 26 Jun 2006 18:29 GMT > I recently stumbled across this idiom. > [quoted text clipped - 9 lines] > redundant to me. Doesn't the synchronized static method use the class > object? It does - but it's own class. If this code is not in class ARTClientThread then it has different semantics than the version without the block.
Kind regards
robert
Chris Uppal - 26 Jun 2006 18:39 GMT > public static synchronized int getThreadCountMax() > { [quoted text clipped - 7 lines] > redundant to me. Doesn't the synchronized static method use the class > object? If getThreadCountMax() is a method at the outer level of ARTClientThread then it is redundant.
If it's a member of any other class (including classes nested inside ARTClientThread) then it's not redundant. In that case, depending on the design of the locking strategy, it could be any of: wasteful, absolutely necessary, or an invitation to create deadlocks.
-- chris
Eric Sosman - 26 Jun 2006 18:51 GMT dmcreyno wrote On 06/26/06 12:59,:
> I recently stumbled across this idiom. > [quoted text clipped - 9 lines] > redundant to me. Doesn't the synchronized static method use the class > object? If the method is part of the ARTClientThread class, the same Class object is locked redundantly.
If the method is part of some other class, that class' Class object and ARTClientThread.class are distinct and both are locked once each. This isn't "redundant," but it seems "useless."
Has this code been victimiz-- er, "improved" by one of those semi-automatic refactoring tools? The gadgets that take the thought out of coding and produce thoughtless code?
 Signature Eric.Sosman@sun.com
dmcreyno - 26 Jun 2006 18:54 GMT As others have asked, it is a method on ARTClientThread. So, redundant it is.
> I recently stumbled across this idiom. > [quoted text clipped - 9 lines] > redundant to me. Doesn't the synchronized static method use the class > object? Mark Space - 27 Jun 2006 00:30 GMT > As others have asked, it is a method on ARTClientThread. So, redundant > it is. That's wild. Does Java sychronized keyword have code to check "don't lock this if I have already locked this?" 'Cause otherwise it's an instant deadlock...
>> I recently stumbled across this idiom. >> [quoted text clipped - 9 lines] >> redundant to me. Doesn't the synchronized static method use the class >> object? Chris Smith - 27 Jun 2006 00:49 GMT > > As others have asked, it is a method on ARTClientThread. So, redundant > > it is. > > That's wild. Does Java sychronized keyword have code to check "don't > lock this if I have already locked this?" 'Cause otherwise it's an > instant deadlock... Locks are recursive in Java; hence, no deadlock.
 Signature Chris Smith - Lead Software Developer / Technical Trainer MindIQ Corporation
Mark Space - 27 Jun 2006 18:23 GMT >>> As others have asked, it is a method on ARTClientThread. So, redundant >>> it is. [quoted text clipped - 3 lines] > > Locks are recursive in Java; hence, no deadlock. I don't see how recursion would help, but I'll take your word on the no deadlock bit.
Do you mean "able to be used in recursive algorithms" maybe? I think that might be re-entrant or something.
Chris Smith - 27 Jun 2006 18:52 GMT > > Locks are recursive in Java; hence, no deadlock. > [quoted text clipped - 3 lines] > Do you mean "able to be used in recursive algorithms" maybe? I think > that might be re-entrant or something. A recursive lock is a lock that keeps track of who is holding it, and a lock count. When the holder tries to grab the lock a second time, it simply increments that counter. When the holder releases the lock, then, it decrements that counter. Only when the counter reaches zero is the lock actually released.
This does solve this specific deadlock problem. Of course, it's still possible to write deadlocks in Java, but it does require two locks.
 Signature Chris Smith - Lead Software Developer / Technical Trainer MindIQ Corporation
Mark Space - 27 Jun 2006 20:43 GMT >>> Locks are recursive in Java; hence, no deadlock. >>> [quoted text clipped - 9 lines] > then, it decrements that counter. Only when the counter reaches zero is > the lock actually released. Ok, first time I've heard that term, applied to mutex or semaphores anyway. Thanks!
Red Orchid - 26 Jun 2006 22:48 GMT "dmcreyno" <david.mcreynolds@gmail.com> wrote or quoted in Message-ID: <1151341182.628313.267040@i40g2000cwc.googlegroups.com>:
> public static synchronized int getThreadCountMax() > { [quoted text clipped - 7 lines] > redundant to me. Doesn't the synchronized static method use the class > object? From a different standpoint ..
In the above example, 'threadCountMax' seems to be 'int' variable. As I know as, the operation that reads/writes 'int' variable is atomic. Therefore, 'synchronized' will not be required.
Mike Schilling - 27 Jun 2006 01:43 GMT > "dmcreyno" <david.mcreynolds@gmail.com> wrote or quoted in > Message-ID: <1151341182.628313.267040@i40g2000cwc.googlegroups.com>: [quoted text clipped - 16 lines] > As I know as, the operation that reads/writes 'int' variable is atomic. > Therefore, 'synchronized' will not be required. If there's another method, say
public static synchronized int incrementThreadCount() { threadCountMax++; }
Both synchronized blocks are required to ensure that changes to the value made in one thread are seen in another.
Red Orchid - 27 Jun 2006 04:09 GMT "Mike Schilling" <mscottschilling@hotmail.com> wrote or quoted in Message-ID: <%a%ng.123621$dW3.96134@newssvr21.news.prodigy.com>:
> If there's another method, say > [quoted text clipped - 5 lines] > Both synchronized blocks are required to ensure that changes to the value > made in one thread are seen in another. I want that you go into details about why both synchronized blocks are required.
My point is:
First, let's consider 'case_a'.
<case_a> class Thread_1 .... { int getValue() { return some_Int_Var; // <- #1 } }
class Thread_2 .... { void setValue() { ... some_Int_Var = ....; // <- #2 ... } }
Thread_1 can not intercept the operation #2 while #2 writes some value on 'some_Int_Var'.
Thread_2 can not intercept the operation #1 while #1 is working because #1 is atomic. And #1 do not produce any effect on #2 because #1 is only reading of 'some_Int_Var'. </case_a>
Next,
<case_b> class Thread_3 .... { void setValue() { ... some_Int_Var = ... + some_Int_Var; // <- #3 ... } }
class Thread_4 .... { void setValue() { ... some_Int_Var = ... + some_Int_Var; // <- #4 ... } }
Both #3 and #4 are not atomic operations. That is, Thread_4 can intercept the operation #3 while #3 is working. In ths case, both synchronized blocks are required. </case_b>
Last,
<case_c> class Thread_5 .... { int getValue() { return some_Int_Var; // <- #5 } }
class Thread_6 .... { void setValue() { ... some_Int_Var = ... + some_Int_Var; // <- #6 ... } }
class Thread_7 .... { void setValue() { ... some_Int_Var = ... + some_Int_Var; // <- #7 ... } }
#5 do not need 'synchronized block'. But, both #6 and #7 need synchronized blocks. </case_c>
Original poster's example is the case of #1 or #5. Therefore, synchronized block will not be required with the example.
Chris Smith - 27 Jun 2006 04:34 GMT > I want that you go into details about why both synchronized blocks > are required. The concurrency model of Java is specified very loosely. There are certain pairs of operations that may occur in which it may be inferred that one operation happened before the other. For example, if two operations happen in the same thread, then it may be inferred that there is an order to the operations (the same order in which they appear in the code). Also, if threads get and release a monitor via synchronized blocks, then this allows you to validly conclude that one thread released the synchronized block before the other thread got it. There are a few more. These pairs of operations are listed in the language specification. Outside of these pairs of operations, though, the implementation is free to act as if operations happened in any order whatsoever, regardless of what the order actually is. The result is that if threads never do certain things, then the implementation may act as if one thread hasn't yet done the stuff that it's done, from the perspective of the other thread.
So that's the formal reason. However, there is another question. Why is it specified that way? It's specified that way to give flexibility to the implementation. Implementations have to deal with complexity that you couldn't dream of sometimes. Many CPUs will feel free to change the order in which they execute various instructions of your code, unless they are explicitly told not to... and telling them not to may result in severe performance costs due to pipeline stalls. The JVM might even be running on a distributed network, where it's very expensive to go check if some other thread has modified an object, and you'd rather put it off until you absolutely need to do so. Not to mention that future technologies will have even greater challenges. I actually see it as quite likely that future multiprocessing CPU architectures will be more like distributed applications, in which it's more expensive to access memory that's "close" to the CPU than memory that's far away. That leads to more scalable designs, if you can figure out the compilation challenges of managing how to put things closer to their respective CPUs in memory. The JVM concurrency model is written as loosely as possible to let you write correct code by applying synchronization primitives, but otherwise give the implementation as much flexibility as possible in handling these kinds of issues.
So that's why. If you don't understand, though, the rule of thumb is still pretty simple. Don't assume that you'll see changes made in a different thread unless you enter a synchronized block for a montior that was held by the other thread when the changes were made. Don't assume that other threads can see your changes unless you enter a synchronized block when you make them (and the other threads grab the same monitor before reading).
> Thread_1 can not intercept the operation #2 while #2 writes some > value on 'some_Int_Var'. Atomicity is a different question. The JVM does guarantee that all attempts to read an int will return an actual value, and not some half- way in between thing. However, say that thread 1 writes to an int variable, and then thread 2 reads that same variable. Thread two will get *either* the value before thread 1 wrote to it, *or* the value after, but there's no way to tell which unless there's some synchronized block (or some other way to prove a sequence that's acceptable in the JLS concurrency model) involved.
> Therefore, > synchronized block will not be required with the example. Yes, it will. Things are more complicated than you are assuming, because there is no global order for operations in the program. Things can happen in different orders depending on the observer. Almost like special relativity...
 Signature Chris Smith - Lead Software Developer / Technical Trainer MindIQ Corporation
Chris Uppal - 27 Jun 2006 10:51 GMT > Therefore, > synchronized block will not be required with the example. [This is in addition to what Chris Smith has already explained]
Consider this:
privant int value;
public int getValue() { return value; }
public void aMethod() { while (true) doSomethingWith(getValue()); }
Since getValue() is not synchronised, and uses no sychronisation blocks, the compiler (which really means JITer) is perfectly within its right to first inline the method call, as if the original Java source was:
public void aMethod() { while (true) doSomethingWith(value); }
and then to "notice" that value does not change during the loop, and to optimise the field access into a local variable access:
public void aMethod() { int __xyz123 = value; while (true) doSomethingWith(__xyz123); }
So, in another thread /does/ change value, the loop will never "see" the new value.
Synchronisation acts not only to control the (potentially very loose) coupling between the state of RAM seen by different CPUs, but also to inform the optimiser of the limits to what it is allowed to assume.
-- chris
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|