Best practices for thread safety in Java?

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.
LVL 4
Javin007Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

krakatoaCommented:
Take a look around the java.util.concurrent API.
0
dpearsonCommented:
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
Javin007Author Commented:
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
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

dpearsonCommented:
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
Javin007Author Commented:
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
dpearsonCommented:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Javin007Author Commented:
Hrm. Certainly seems easy enough.  I'll have to come back if I ever figure out what the "need to" reason was.
0
dpearsonCommented:
Sounds good - we'll be here :)

Doug
0
mccarlIT Business Systems Analyst / Software DeveloperCommented:
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
Javin007Author Commented:
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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Java

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.