Synchronized Statements – Concurrency: Part I

Synchronized Statements

Whereas execution of synchronized methods of an object is synchronized on the lock of the object, the synchronized statement allows execution of arbitrary code to be synchronized on the lock of an arbitrary object. The general form of the synchronized statement (also called synchronized block) is as follows:

Click here to view code image

synchronized
(
object_reference_expression
) {
code_block
 }

The object reference expression must evaluate to a non-null reference value; otherwise, a NullPointerException is thrown. The code block is usually related to the object whose lock is acquired. This is analogous to a synchronized method, where the execution of the method is synchronized on the lock of the current object. The following code is equivalent to the synchronized increment() method in the CounterX class in Example 22.4:

Click here to view code image

public void increment() {
  synchronized(this) {                                // Synchronized statement
    System.out.println(Thread.currentThread().getName()
        + “: current: ” + counter + ” new:” + ++counter);
  }
}

Once a thread has entered the code block after acquiring the lock on the specified object, no other thread will be able to execute the code block, or any other code requiring the same object lock, until the lock is released. This happens when the execution of the code block completes normally or an uncaught exception is thrown. In contrast to synchronized methods, this mechanism allows fine-grained synchronization of code on arbitrary objects.

The object reference expression in the synchronized statement is mandatory. A class can choose to synchronize the execution of a part of a method by using the this reference and putting the relevant code of the method in the synchronized statement.

The curly brackets of the block cannot be omitted, even if the code block has just one statement.

Click here to view code image

class SmartClient {
  BankAccount account;
  // …
  public void updateTransaction() {
    synchronized (account) {       // (1) synchronized statement
      account.update();            // (2)
    }
  }
}

In the example above, the code at (2) in the synchronized statement at (1) is synchronized on the BankAccount object. If several threads were to concurrently execute the method updateTransaction() on an object of SmartClient, the statement at (2) would be executed by one thread at a time only after synchronizing on the BankAccount object associated with this particular instance of SmartClient.

Inner classes can access data in their enclosing context (§9.1, p. 491). An inner object might need to synchronize on its associated outer object in order to ensure integrity of data in the latter. This is illustrated in the following code where the synchronized statement at (5) uses the special form of the this reference to synchronize on the outer object associated with an object of the inner class. This setup ensures that a thread executing the method incr() in an inner object can only access the private double field count at (2) in the synchronized statement at (5) by first acquiring the lock on the associated outer object. If another thread has the lock of the associated outer object, the thread in the inner object has to wait for the lock to be released before it can proceed with the execution of the synchronized statement at (5). However, synchronizing on an inner object and on its associated outer object are independent of each other, unless enforced explicitly, as in the following code:

Click here to view code image

class Outer {                      // (1) Top-level Class
  private double count;            // (2)
  protected class Inner {          // (3) Non-static member Class
    public void incr() {           // (4)
      synchronized(Outer.this) {   // (5) Synchronized statement on Outer object
        ++count;                   // (6)
      }
    }
  }
}

A synchronized statement can also be specified on a class lock:

Click here to view code image

synchronized
(
<class_name>
.class
) {
<code_block>
}

The statement synchronizes on the lock of the object denoted by the reference class_ name.class. This object (of type Class) represents the class at runtime. A static synchronized method classAction() in class A is equivalent to the following declaration:

Click here to view code image

static void classAction() {
  synchronized (A.class) {       // Synchronized statement on class A
    // …
  }
}

In summary, a thread can acquire a lock by carrying out the following actions:

  • By executing a synchronized instance method of an object of a class.
  • By executing a static synchronized method of a class (in which case, the object is the Class object representing the class in the JVM).
  • By executing the body of a synchronized statement that synchronizes either on an object of a class, or on the Class object representing a class in the JVM.
  • Intrinsic locking does not provide any guarantees as to which thread among the waiting threads will acquire the intrinsic lock.
  • Intrinsic lock acquisition is rigid: Either the thread acquires the intrinsic lock immediately if it is available, or the thread has to wait its turn among the waiting threads. There is no other lock acquisition policy—for example, timed waiting.
  • An intrinsic lock is acquired and released in the same synchronized code—for example, in a synchronized method. It cannot be acquired or released in separate methods.

Leave a Reply

Your email address will not be published. Required fields are marked *