0

In this java tutorial there's some code that shows an example to explain the use of the synchronized keyword. My point is, why I shouldn't write something like this:

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(c1) {
            c1++;
        }
    }

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

Without bothering create lock objects? Also, why bother instantiate that lock objects? Can't I just pass a null reference? I think I'm missing out something here.

Also, assume that I've two public synchronized methods in the same class accessed by several thread. Is it true that the two methods will never be executed at the same time? If the answer is yes, is there a built-in mechanism that prevents one method from starvation (never been executed or been executed too few times compared to the other method)?

Gray
  • 115,027
  • 24
  • 293
  • 354
JoulinRouge
  • 456
  • 4
  • 18

3 Answers3

4

As @11thdimension has replied, you cannot synchronize on a primitive type (eg., long). It must be a class object.

So, you might be tempted to do something like the following:

Long c1 = 0;
public void incC1() {
  synchronized(c1) {
    c1++;
  }
}

This will not work properly, as "c1++" is a shortcut for "c1 = c1 + 1", which actually assigns a new object to c1, and as such, two threads might end up in the same block of synchronized code.

For the lock to work properly, the object being synchronized upon should not be reassigned. (Well, maybe in some rare circumstances where you really know what you are doing.)

You cannot pass a null object to the synchronized(...) statement. Java is effectively creating semaphores on the ref'd object, and uses that information to prevent more than one thread accessing the same protected resource.

You do not always need a separate lock object, as in the case of a synchronized method. In this case, the class object instance itself is used to store the locking information, as if you used 'this' in the method iteslf:

public void incC1() {
    synchronized(this) {
      c1++;
    }
}
Jamie
  • 1,888
  • 1
  • 18
  • 21
  • thank you! can you provide a source for "Java is effectively creating semaphores on the ref'd object" ? – JoulinRouge Sep 07 '16 at 07:23
  • It seems 'semaphore' was not quite the right term, but rather, 'monitor lock' or 'instrinsic lock'. https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html – Jamie Sep 07 '16 at 15:51
3

First you can not pass primitive variable to synchronized, it requires a reference. Second that tutorial is just a example showing guarded block. It's not c1,c2 that it's trying to protect but it's trying to protect all the code inside synchronized block.

JVM uses Operating system's scheduling algorithm.

What is the JVM Scheduling algorithm?

So it's not JVM's responsibility to see if threads are starved. You can however assign priority of threads to prefer one over other to execute.

Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority. Each thread may or may not also be marked as a daemon. When code running in some thread creates a new Thread object, the new thread has its priority initially set equal to the priority of the creating thread, and is a daemon thread if and only if the creating thread is a daemon.

From:https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html

If you're concerned about this scenario then you have to implement it yourself. Like maintaining a thread which checks for starving threads and as time passes it increases the priority of the threads which have been waiting longer than others.

Yes it's true that two method which have been synchronized will never be executed on the same instance simultaneously.

Community
  • 1
  • 1
11thdimension
  • 10,333
  • 4
  • 33
  • 71
1

Why bother instantiate that lock objects? Can't I just pass a null reference?

As others have mentioned, you cannot lock on long c1 because it is a primitive. Java locks on the monitor associated with an object instance. This is why you also can't lock on null.

The thread tutorial is trying to demonstrate a good pattern which is to create private final lock objects to precisely control the mutex locations that you are trying to protect. Calling synchronized on this or other public objects can cause external callers to block your methods which may not be what you want.

The tutorial explains this:

All updates of these fields must be synchronized, but there's no reason to prevent an update of c1 from being interleaved with an update of c2 — and doing so reduces concurrency by creating unnecessary blocking. Instead of using synchronized methods or otherwise using the lock associated with this, we create two objects solely to provide locks.

So they are also trying to allow updates to c1 and updates to c2 to happen concurrently ("interleaved") and not block each other while at the same time making sure that the updates are protected.

Assume that I've two public synchronized methods in the same class accessed by several thread. Is it true that the two methods will never be executed at the same time?

If one thread is working in a synchronized method of an object, another thread will be blocked if it tries the same or another synchronized method of the same object. Threads can run methods on different objects concurrently.

If the answer is yes, is there a built-in mechanism that prevents one method from starvation (never been executed or been executed too few times compared to the other method)?

As mentioned, this is handled by the native thread constructs from the operating system. All modern OS' handle thread starvation which is especially important if the threads have different priorities.

Gray
  • 115,027
  • 24
  • 293
  • 354