1

So in Java I know that you can use the so-called "intrinsic lock" of objects to create mutual exclusion regions as well as to ensure memory visibility. Java makes it particularly easy to implicitly lock on the intrinsic lock of the this object with some syntax sugar like:

public class Foo{
    public synchronized doFoo(){
        //doFoo is executed in an implicit synchronized block
        //on the 'this' object
    }
}

This is understandable and accepted practice for guarding the member fields of many objects. What I don't know is whether the above is okay when the object being locked on is a Thread object. E.g., are there any reasons to avoid the following?

public class Bar extends Thread{ //notice the 'extends Thread' here
    public synchronized doBar(){
        //doBar is executed in an implicit synchronized block
        //on the 'this' object
    }
}

For now, I'm going to stick with something I know is more safe, e.g.:

public class Baz extends Thread{ //notice the 'extends Thread' here
    private final Object explicitLockObject = new Object();

    public doBaz(){
        synchronized(explicitLockObject){
            //doBaz impl
        }
    }
}

My concerns would be two-fold with option #2 (the Bar example):

  1. Is there existing jvm code or Java convention regarding synchronizing on the Thread itself that might conflict with such a locking policy?
  2. Locking on this generally implies that access of that object should always be guarded by that object's intrinsic lock. In the case of a Bar thread, that means we're implying that any time you touch a Bar thread, you should synchronize on the instance. That seems like it could end up causing some other thread to block unnecessarily (or even dangerously) until Bar completes/exits.

Are the above valid concerns? I feel like I need a Brian Goetz beacon for this one :-)

codeCogs
  • 127
  • 7
  • 3
    1. Well, there is a convention that you shouldn't extend `Thread` in the first place: implement a `Runnable`, pass it to the `Thread` as a constructor parameter (see [this question](http://stackoverflow.com/questions/541487/implements-runnable-vs-extends-thread)). Now, would you be concerned about synchronizing on the `Runnable`? – Andy Turner Aug 22 '16 at 15:43
  • Yes, I'm aware of that convention. To be honest, this isn't originally my code, and I'm not planning to refactor. I know `Runnable` would be fine since it comes with no state baggage. But alas! That's not what I have and I'm curious to know the answer to this. – codeCogs Aug 22 '16 at 15:46
  • 1
    I would recommend that developers prefer the new classes in concurrency package and not use raw Threads. Multi-threaded code is hard enough to write. – duffymo Aug 22 '16 at 15:48
  • 3
    I would avoid synchronizing on a `Thread` object if possible. Or indeed on any publicly visible object. But even more so with `Thread`, as some methods of `Thread` will also use the intrinsic lock themselves. – biziclop Aug 22 '16 at 15:59
  • 3
    Using the intrinsic lock also allows client code to influence the synchronization policy of your object (`synchronized (theBarThreadObj) { … }`). Using an internal `final` object prevents this. – Sean Bright Aug 22 '16 at 15:59
  • No it isn't OK, and the [Javadoc](https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join(long)) specifically says so. – user207421 Sep 02 '16 at 04:29
  • @EJP: Thanks for the link, but what I see there says "It is recommended that applications not use wait, notify, or notifyAll on Thread instances", which I don't think necessarily mean the intrinsic lock is off limits. – codeCogs Sep 04 '16 at 22:55

1 Answers1

2

... are there any reasons to avoid [locking on a Thread instance]?

So if you look at the Thread class, you can see that there are are a number of instance synchronized methods. They include:

  • start()
  • stop() (deprecated)
  • join(long) (which is called from join())
  • join(long, int)

That seems like it could end up causing some other thread to block unnecessarily (or even dangerously) until Bar completes/exits.

Right, so anyone calling the above methods would block with other synchronized methods. Obviously only the join(...) methods would actual be a problem and they should't return until the Thread finishes anyway.

As stated in the javadocs for Thread.join(...):

As a thread terminates the this.notifyAll() method is invoked. It is recommended that applications not use wait(), notify(), or notifyAll() on Thread instances.

I see no warnings about synchronized anywhere in the Thread source.

Also quick glance through ThreadPoolExecutor shows that it at least doesn't synchronize on the Runnable or Callable instances which would be pretty bad so at least with a standard ExecutorService you should be good.

So I guess the short answer is that although it maybe not recommended, in a pinch synchronizing on the Thread should work. As you mention, it would be better for you to implement Runnable and lock on it instead of extend Thread.

Hope this helps.

Gray
  • 115,027
  • 24
  • 293
  • 354
  • 1
    Very helpful- I would consider the warnings about `wait()`, `notify()` etc. to be some evidence that using a `Thread` object's multithreading utilities is a little different than using those of any other `Object`, but the lack of a warning about the intrinsic monitor itself does suggest that it _might_ be ok. Upvoted, but I'll leave a few more days for someone to swoop in with some source/argument that definitively addresses the issue rather than relying on lack of a warning (when are docs perfectly complete :-) ?). If nobody jumps in soon, I'll mark as answer. – codeCogs Sep 04 '16 at 23:02