Solved

Best practices for thread safety in Java?

Posted on 2014-11-18
10
343 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 16

Expert Comment

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

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
 
LVL 26

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
Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 26

Accepted Solution

by:
dpearson earned 500 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 26

Expert Comment

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

Doug
0
 
LVL 35

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

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Introduction This article is the first of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article explains our test automation goals. Then rationale is given for the tools we use to a…
This is an explanation of a simple data model to help parse a JSON feed
Viewers will learn about if statements in Java and their use The if statement: The condition required to create an if statement: Variations of if statements: An example using if statements:
The viewer will learn how to implement Singleton Design Pattern in Java.

744 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

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now