1

In this oracle example of intrinsic locks and many more, the monitor object is never declared as volatile, final or nor it has any distinction from any other regular object

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}

There are plenty of questions that debate volatile versus synchronization blocks

and immutable objects

As a side note, I understand this subtle difference between declaring an object final versus immutability why-can-final-object-be-modified and why declaring the lock object as final would not make it immutable.

However, we have the famous pattern of the singleton class lazy initialization where the use of the volatile variables is essential.

public class SingletonDemo {
    private static volatile SingletonDemo instance;
    private SingletonDemo() { }

    public static SingletonDemo getInstance() {
        if (instance == null ) {
            synchronized (SingletonDemo.class) {
                if (instance == null) {
                    instance = new SingletonDemo();
                }
            }
        }

        return instance;
    }
}

which in the above code example uses the Class object as lock.

Since for an object which is accessed by multiple threads you need to use some mechanism as above to ensure atomic access, why is that for intrinsic lock object there is no need for any special treatment?

Community
  • 1
  • 1
Radu Ionescu
  • 3,462
  • 5
  • 24
  • 43
  • If you use IntelliJ (which I highly recommend), it will tell you to declare the locks as `final`. The main reason I believe is to prevent accidental locking on different objects. – ZhongYu Feb 28 '16 at 19:38
  • 1
    Using `final` on a lock object is good practice, but not actually required by the memory model. – markspace Feb 28 '16 at 19:39
  • 1
    @markspace -- unless :) the class is designed to survive unsafe publication. e.g. all java.util.concurrent classes (last time I check). it's really unnecessary, but the author designed them to sustain abuse. – ZhongYu Feb 28 '16 at 19:56

3 Answers3

1

These locks don't need special treatment because the MsLunch object itself needs to be published before it can be seen by any additional threads.

public class MyMain {
  public static void main(String... args) {
    MsLunch lunch = new MsLunch();
    // ...

This is thread safe because local variables ("lunch") are not visible to more than one thread.

Next the class below makes the local reference visible to all threads in the system. When that happens we need to use volatile. The volatile keyword effectively creates a memory barrier that publish the object safely. This includes all writes made before the assignement including writes made internally when constructing the object.

C.f. Safe Publication

public class MyMain {

  public static volatile MsLunch publicLunch;

  public static void main(String... args) {
    MsLunch lunch = new MsLunch();
    publicLunch = lunch;
    //...
  }
}
Community
  • 1
  • 1
markspace
  • 10,621
  • 3
  • 25
  • 39
0

It probably should be final. But final isn't anything really special- its only required in one special case (referencing a variable declared inside a function into an anonymous class). Any other case final is simply a reminder for the programmer to not overwrite the variable- you can remove every other use of the word final in your program and it will work perfectly. You're right, a programmer could assign to it and then cause problems. But if he doesn't, there's no issue. So go ahead and use final when you create one, but it isn't necessary for the program to compile.

As for static- depends on the usecase. Do you want to monitor all instances of a class, or each instance independently? In the first case, you use static in the second case you don't.

Volatile isn't needed because the object isn't actually being changed by the multiple threads. Its being synchronized on. This is completely different, and an older part of the Java language than volatile. There's no need to make the variable volatile as you won't be altering it, and the internal data structures used to monitor on an object already know they need to be thread safe (and in a stronger manner than volatile promises).

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
  • This data structure synchronization seems a bit confusing on how it is actually achieved. If the lock object is created by/in Thread1, but Thread2 is the first that actually reaches the synchronized block how can it see the same object as Thread1 before Thread1 copies it back from the thread local stack and this without any special treatment of the lock object (not that volatile, static or final could make any difference). Or this is not something dynamic and at compile time the `synchronized` block modifies the code to prevent this case. – Radu Ionescu Feb 28 '16 at 21:43
  • Well in the code you posted, the lock is created when the MsLunch is created. Since inc1 and inc2 are member functions of MsLunch, they can't be called until after MsLunch is created. So there is no way for there to be a conflict here. – Gabe Sechan Feb 28 '16 at 21:44
  • But yes if you had code that looked like launchThread1(); launchThread2(); createLock(); where thread 1 and 2 used the lock, that could cause all sorts of problems. The lock needs to be created before more than 1 thread can access it- createLock(); launchThread1(); launchThread2(); – Gabe Sechan Feb 28 '16 at 21:47
  • So the only mechanism that ensures the synchronized block actually ensures atomic execution is based on creating the lock before in a thread which is guaranted to execute before all other subsequent threads that use that lock, either by createLock(); launchThread1(); launchThread2(); or as in my code example (which is most of the times used) – Radu Ionescu Feb 28 '16 at 21:52
  • If this is the case it seems that is not always guaranteed and there are cases where it can fail – Radu Ionescu Feb 28 '16 at 21:53
  • So as a particular case using MsLunch with Thread1 having low priority and Thread2 having higher priority, is it possible to interupt Thread1 before it completes creating MsLunch and the lock, to get a null pointer exception in Thread2 when accessing the MsLunch object? With all the bad consequences that come from this. – Radu Ionescu Feb 28 '16 at 21:56
  • This answer is really misleading. Both `final` and `static` are special. They have memory visibility semantics. Please see this SO post on the Safe Publication design pattern. http://stackoverflow.com/questions/801993/java-multi-threading-safe-publication – markspace Feb 28 '16 at 22:49
  • @markspace They have uses, but they aren't special in the context of this discussion. Especially final. – Gabe Sechan Feb 28 '16 at 22:55
  • `final` is special in the context of the discussion because Radu is asking about memory visibility. That's why he lists final, static, and volatile in the title of the question. It's pretty obvious if you've done any multi-threaded programming in Java. And he's right: he needs some form of memory visibility or his program isn't going to be able to initialize its internal fields correctly. – markspace Feb 28 '16 at 22:58
  • @markspace No, it isn't. declaring a variable final is not needed in this case. Final only means that the reference must be created at init time and that it cannot be assigned after that. Its useful for telling other programmers this variable shouldn't be overwritten, but if those semantics are followed anyway the keyword is not needed for correct functioning. You're just wrong here. – Gabe Sechan Feb 28 '16 at 23:01
  • No I'm not. Go read the spec. Or see Brian Goetz's *Java Concurrency in Practice*. Everyone know that `final` fields in Java will make a referenced object visible to all threads. Ask a question about here on SO if you don't believe me. – markspace Feb 28 '16 at 23:04
  • @markspace Yes, you are. Read the code in his post. There is no necessity to make it final. Good practice, yes. Necessity, no. I don't need to ask a question, I've been writing multithreaded code in Java for a decade. – Gabe Sechan Feb 28 '16 at 23:06
  • So can you quote the part of Java language spec that says that final fields aren't "special?" – markspace Feb 28 '16 at 23:07
  • @markspace Take his code up on top. Write a simple framework around it. You'll see the synchronization works despite the fact the lock isn't final. That's proof enough. Its a freaking Oracle example app. You either don't know what you're talking about, or charitably have just managed to thorughly confuse yourself. – Gabe Sechan Feb 28 '16 at 23:08
  • No, the OP is right. The fields `lock1` and `lock2` could be read as `null` unless there's some form of memory visibility. Their initial assignment isn't guaranteed to be visible. That's why in Java you have to use safe publication. Please get that Brian Goetz book and read it. – markspace Feb 28 '16 at 23:11
  • No they really can't. Just ran the program, it works. Please stop spreading misinformation – Gabe Sechan Feb 28 '16 at 23:12
  • Multi-threaded programs tend to work if you try them under light load. They'll fail unexpectedly under heavy load with lots of threads. Surely you know that? And I'm disappointed that you're tying to tell me I'm wrong with out a single reference to the spec or any other source. Poor form. – markspace Feb 28 '16 at 23:19
  • Lad would have 0 effect on this problem. Have you even run it yet? Until you come back with a test that shows out not working, the fact that this is a freaking Oracle example program that is testably working means it isn't worth digging through spec docs – Gabe Sechan Feb 28 '16 at 23:22
-1

In this oracle example of intrinsic locks and many more, the monitor object is never declared as volatile, final or nor it has any distinction from any other regular object.

That's not true. See below.

Since for an object which is accessed by multiple threads you need to use some mechanism as above to ensure atomic access, why is that for intrinsic lock object there is no need for any special treatment?

It does have special treatment. It is synchronised on.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • This _synchronized on_ is what I do not understand, Since until it is used the lock object is as same as any other object(it can be cached by the thread, created first in thread local memory,etc) and I can freely swap it (there is nothing that prevents me from saying `lockObject = new Object()` anywhere else in the code if I am using `synchronized(lockObject)` and it has to be be visible in other threads) – Radu Ionescu Feb 28 '16 at 20:25