Solved

Best practices for thread safety in Java?

Posted on 2014-11-18
10
369 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
10 Comments
 
LVL 16

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
Get 15 Days FREE Full-Featured Trial

Benefit from a mission critical IT monitoring with Monitis Premium or get it FREE for your entry level monitoring needs.
-Over 200,000 users
-More than 300,000 websites monitored
-Used in 197 countries
-Recommended by 98% of users

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

Optimize your web performance

What's in the eBook?
- Full list of reasons for poor performance
- Ultimate measures to speed things up
- Primary web monitoring types
- KPIs you should be monitoring in order to increase your ROI

Question has a verified solution.

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

Today, the web development industry is booming, and many people consider it to be their vocation. The question you may be asking yourself is – how do I become a web developer?
Part One of the two-part Q&A series with MalwareTech.
Viewers will learn one way to get user input in Java. Introduce the Scanner object: Declare the variable that stores the user input: An example prompting the user for input: Methods you need to invoke in order to properly get  user input:
Six Sigma Control Plans

636 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