Win a copy of Murach's Python Programming this week in the Jython/Python forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

accessing peers in synchronized blocks  RSS feed

 
sarvesh meens
Ranch Hand
Posts: 43
Firefox Browser Java Netbeans IDE
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
It is a guideline that a method in another class should not be accessed with in synchronized blocks.

eg:

But there are situations,where this is the required behaviour.

eg:

What should one do in such cases?

[ added code tags - Jim ]
[ November 21, 2007: Message edited by: Jim Yingst ]
 
Jim Yingst
Wanderer
Sheriff
Posts: 18671
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well, the answer varies a lot. If you're the one designing all the involved classes (or if you at least have some influence over how they're designed), then typically it's best to try to design the classes so this isn't necessary. Of course this may not be possible, either because you don't control all the relevant code, or for more complex design issues.

In the example given, my first thought is to make a Transaction class that will handle the interaction between the two classes. Maybe each class will implement a Transactable interface where each Transactable object may be involved in only one Transaction at a time. Associating each object with a Transaction would require brief synchronization on that object. Then executing the transaction would sync on the Transaction instance. And finally, disassociating the two objects from the Transaction would require anothe brief sync on the respected objects. Each of these sync locks would not overlap, so no chance of deadlock.

That's getting somewhat complex though - maybe a simpler solution exists. It's hard to say without a lot more detail on what the classes are trying to do.

Let's consider why (and when) one should avoid calling a foreign method within sync blocks. I think there are two main reasons. One is that anytime a class requires syncronization outside that class, it's an invitation for coders to overlook that requirement. A programmer writing class A which needs to use class B is often more familiar with A (written by them) than B (written by someone else). Thus, people routinely overlook external synchronization requirements. E.g. a "thread safe" class like a synchronizedList() implementation still requires a sync block if you iterate over it - but how often to programmers overlook this? So, it's best if a class is designed to avoid this where possible. Sometimes, however, it's just not possible.

The second reason to avoid calling a foreign method within sync blocks is that if that foreign method ends up doing any synchronization of its own, there's a chance of deadlock. That's a pretty serious problem - but it depends a lot on how much you know about this other method you're calling. Who wrote the method, and is the method overrideable? And who instantiated the object you're using to invoke the method? If the method has well-defined behavior and is not overrideable, and/or if the invoking instance was created by you and/or is not accessible to other threads - those are all things that can give you increased confidence that deadlock will not occur. (Assuming of course that you know that the otherMethod() will not try to do any sync of its own, at least not on any objects that might be locked indefinitely by other threads.) But if anotherClassObject was created elsewhere and passed to you, and especially if its doSomething() method is overrideable, then you may have no way of knowing that some client won't eventually provide an implementation that does some synchronization that creates a deadlock.

As a more concrete example: a StringBuffer uses synchronization internally. In theory this might create deadlock if you call a StringBuffer method from within a synchronized block (synced on something besides the StringBuffer). But StringBuffer is a final class that never syncs on any instance other than itself. And when you create a StringBuffer, there's no way to give it a reference to any mutable object that you might be trying to sync on yourself. So there's really no way for a StringBuffer to try to sync on something else that will create a deadlock - it's perfectly safe to call any StringBuffer method from within a sync block. The methods could block indefinitely if another thread is holding a lock on the StringBuffer and won't release it for some other reason (presumably some sort of abysmally bad programming) - but we can't defend against all possible stupid behavior by other programmers. Calling a StringBuffer method from syncronized code is pretty darn safe, all in all. Even if it is a nested lock.

But, if you call some external method that's designed to be overridden - like, say, a Runnable's run() method - then you're asking for trouble. Unless that Runnable was instantiated in this very class and you know exactly what it's doing. And bear in mind that if a class doesnt' actually prevent being subclassed (most often with final) then you should probably assume that it will be subclassed. At least, if you're releasing your code to be used by anyone other than yourself.

So I guess I didn't really answer the question, so much as say "try not to do that" and "if you really must do that, under certain circumstances it may still be OK". But I hope that's still of some use.
 
sarvesh meens
Ranch Hand
Posts: 43
Firefox Browser Java Netbeans IDE
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The equivalent of anotherClassObj.doSomething() in my code is



The class in which this synchronized block occurs is the TimerTask.
Considering the fact that I dont expect a over-ridden version of Timer,am placing the call to timer with-in synchronized block

Originally posted by Jim Yingst:

In the example given, my first thought is to make a Transaction class that will handle the interaction between the two classes. Maybe each class will implement a Transactable interface where each Transactable object may be involved in only one Transaction at a time. Associating each object with a Transaction would require brief synchronization on that object. Then executing the transaction would sync on the Transaction instance. And finally, disassociating the two objects from the Transaction would require anothe brief sync on the respected objects. Each of these sync locks would not overlap, so no chance of deadlock.


Am unable to follow the solution.
Code-snippets will be helpful.

Originally posted by Jim Yingst:

So I guess I didn't really answer the question, so much as say "try not to do that" and "if you really must do that, under certain circumstances it may still be OK". But I hope that's still of some use.


Not just some use. It was immensely useful.
Thnaks Jim.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!