Go Premium for a chance to win a PS4. Enter to Win

x
?
Solved

Best practices for thread safety in Java?

Posted on 2014-11-18
10
Medium Priority
?
381 Views
Last Modified: 2014-11-19
I've been trying to figure out the best way to go about locking threads while trying to "do stuff."  One of the problems I've come across is that a thread (or threads) may need an extended "lock" while another thread (or threads) are doing their jobs.  As far as I can tell using "synchronized" will only lock a thread for as long as you're within that specific method, and there's no way to span "locks" across methods.  Perhaps I'm just flat doing it wrong, but here's the scenario:  

4 Methods, A, B, C, and D.  

Thread 1 needs to call A, then D making sure B is not called in the meantime.
Thread 2 needs to call C, but needs to make sure that thread 1 is done with at least A, and that A is not run at all while C is running.
Thread 3 needs to call B.  
Thread 4 needs to call D, but needs to make sure Thread 2 has called C and Thread 1 and Thread 3 are not running at all during the process.  

It was making my brain melt, so I wondered if creating a "synchronizer" object that can be passed around from method to method, and possibly even across threads, was a horrible idea?  (In my mind, it seems like it would be.)  

The code I came up with for the "Synchronizer" class is as follows, and thus far I've yet to get a crash or lockup with it, but I believe that's just a fluke (or the threads aren't doing what I believe them to be doing).  

//Theoretically, this should allow objects to be synchronized across methods and threads instead of just within a single method.
//In practice, this will probably just cause a ton of race conditions and problems.
public class Synchronizer {

	//A custom object is needed to sync on.
	private class SyncLockObject {}
	SyncLockObject Lock = new SyncLockObject();
	
	//Track the synchronization of the object manually.
	boolean booLocked = false;
	
	//This is my attempt at a public locking mechanism that will (theoretically) allow thread safety across methods.
	public void lock() {
		//So if they want to turn the lock "on" first, do the system sync.
		synchronized (Lock) {
			//Now, if the thread is this far, but the boolean shows that the sync is already "on" have the thread wait until it's available.
			while (booLocked) {
				//Allow other threads to run if needed.
				Thread.yield();
			}
			//Theoretically, if we're this far, only one thread should have been permitted to get through the system's "synchronized" function.  So flipping the "locked"
			//to "true" then stepping out of the sync should allow any competing threads to get through one at a time and wait for the boolean to be flipped to "false" again.
			booLocked = true;
		} 
	}
	
	public void unLock() {
		//Simply flip the switch to unlock.
		booLocked = false;
	}
	
}

Open in new window




Maybe a more simple concept:  

I have a custom "LinkedList" object.

It's easy enough to synchronize when an object is added or removed from the list.  But what if another thread wants to lock the LinkedList, find an object, make some changes, then pass that object to another thread, allow it to make some changes, and only THEN unlock the list to allow for further additions or removals?  

I may just be doing everything wrong.
0
Comment
Question by:Javin007
10 Comments
 
LVL 17

Expert Comment

by:krakatoa
ID: 40450310
Take a look around the java.util.concurrent API.
0
 
LVL 28

Expert Comment

by:dpearson
ID: 40450613
You don't need to write your own Synchronizer class (writing your own custom locking code is really hard).

I think you can happily use the existing "synchronize" operation - you just need to control the scope of the object used to control the locking.

If your 4 methods (A,B,C,D) are all in the same class, then you can just use synchronized on all of the methods:

public class myClass {
   public synchronized A() { }
   public synchronized B() { }
   public synchronized C() { }
   public synchronized D() { }
}

The lock you get from this code is actually shared among all of the methods.
Internally this code actually is the same as this code:

   public A() {
       synchronized(this) {
            ... Your code
       }
   }

The key is understanding what "synchronized(this)" means.  Synchronized creates a lock on a specific object - not on a method.  In this case the object is "this" - i.e. the instance of the class that contains the methods A,B,C,D.  So if 1 thread is accessing method A, then another thread cannot access any of A,B,C or D.  They are all blocked - because the lock is on the object that contains the methods - not on the methods.

But what if methods A,B,C and D aren't on the same class?  Then what?
In that case you can create a shared object that they use as a common lock.
E.g. You can do:

public class MyLock {
    public final static Object lock = new Object() ; // We just need an object to lock on
}

And then write the methods like this:

public A() {
    synchronized(MyLock.lock) {
         // Your code
    }
}

public B() {
    synchronized(MyLock.lock) {
         // Your code
    }
}

Now method A and method B can't be accessed by 2 threads at the same time, because they each establish a lock on a shared, static object.  If one thread gets that lock, all other threads trying to get the lock will block and wait their turn.

Does that help?

Doug
0
 
LVL 4

Author Comment

by:Javin007
ID: 40450666
Doug:  It's kind of along the lines, but what I'm concerned about is "holding" a lock between methods, and even threads.

For example, you can't do "If blah, then Synchronize, otherwise blah."  I'm trying to think of the exact scenario that got me to this point, but I've been trying to read the greek that is "Concurrency" and my brain is muddled at the moment.  

Also, is it possible to see if an object is currently "locked" without actually locking it yourself?  Like some sort of "is locked" thing?  (Again, can't remember the reason I wanted that.)

Heck, at this point, I can't even remember why I wrote the code in the first place.
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 28

Expert Comment

by:dpearson
ID: 40450743
For example, you can't do "If blah, then Synchronize, otherwise blah."
Do you mean, you can't do this because of some aspect of the problem or Java doesn't allow this?
If it's the second, that's not really true.  You can control synchronize if you wish:

if (blah) {
    synchronized(this) {
          // Only lock if "blah" is true
    }
}

If it's some aspect of the problem, can you explain that further?

If you want to test to see if a lock is already being held, you can do that.  You need to use a slightly finer grained locking mechanism, like ReentrantLock.  It needs a little more care (you need to explicitly unlock it, while synchronize handles the locking and unlocking for you) but it gives you more control - including an "isLocked()" method.

More here:
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html

Doug
0
 
LVL 4

Author Comment

by:Javin007
ID: 40450766
No, one of the things I'm looking for is along the lines of:  

If (A) Then Synchronize
If (B) Then Don't Synchronize
If (C) Then Don't Synchronize
If (D) Then Synchronize

*do stuff*

If Synchronized, then End Synchronize.

I only remember needing a solution to this a short while back, but now I can't think of a specific example as to why I'd need the ability.  But that was why the code (above) was originally written.  It did seem to make this functionality work, but I can't for the life of me remember why I needed it.
0
 
LVL 28

Accepted Solution

by:
dpearson earned 2000 total points
ID: 40451066
OK that all should be fine using regular "synchronized" syntax.

If (A || D) {
   // Synchronize when in A or D
   synchronize(this) {
       doStuff() ;
   }
   // End synchronize happens automatically when we leave the code block
} else {
      // Don't synchronize for B or C
       doStuff() ;
}

// Move doStuff into its own method so you don't repeat the code
private void doStuff() {
}

All I write every day is complex multi-threaded code and I've never needed to go beyond the built in tools.  It's generally just a matter of reorganizing the problem so it fits the available tools, rather than building out your own custom threading tool set (which as I mentioned is exceptionally easy to get wrong - e.g. you'd need a full grasp of the Java Memory Model and other abstract concepts before diving in).

Hope that helps,

Doug
0
 
LVL 4

Author Comment

by:Javin007
ID: 40451084
Hrm. Certainly seems easy enough.  I'll have to come back if I ever figure out what the "need to" reason was.
0
 
LVL 28

Expert Comment

by:dpearson
ID: 40451091
Sounds good - we'll be here :)

Doug
0
 
LVL 36

Expert Comment

by:mccarl
ID: 40451405
Thread 1 needs to call A, then D making sure B is not called in the meantime.
This requirement (if I'm reading it correctly) looks like its the main one that can't be handled by synchronized statements/methods only, since they can only hold the lock for the duration of the method call, ie. there is no way to stop Thread 3 from running method B in between Thread 1's calls to A and D. For this you would need to use a Lock object (such as ReentrantLock) that specifically allows to be locked in one method (Method A) and unlocked in a different method (Method D). Then Method B also tries to obtain the lock at the start and unlocks it at the end, and you get the synchronization that you describe above.

If this point truly is a requirement that needs to be met, maybe you can raise a new question to discuss this in more detail.
0
 
LVL 4

Author Comment

by:Javin007
ID: 40452108
McCarl:  I'll be looking at the ReentrantLock that you mention.  Could you peruse the code I posted, and see if this is a "sufficient" object to do this?
0

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
Although it can be difficult to imagine, someday your child will have a career of his or her own. He or she will likely start a family, buy a home and start having their own children. So, while being a kid is still extremely important, it’s also …
The viewer will learn how to implement Singleton Design Pattern in Java.
Starting up a Project
Suggested Courses

972 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question