Imprint | Privacy Policy

MX in Java

(Usage hints for this presentation)

IT Systems, Summer Term 2024
Dr. Jens Lechtenbörger (License Information)

1. Introduction

1.1. Thread Terminology

1.2. Thread States

1.3. Java Threads

1.4. Races

1.5. Mutual Exclusion

2. Monitors

2.1. Monitor Idea

  • Monitor ≈ instance of class with methods and attributes
  • Equip every object (= class instance) with a lock
    • Automatically
      • Call lock() when method is entered
        • As usual: Thread is blocked if lock is already locked
        • Thus, automatic MX
        • We say that executing thread entered the monitor or executes inside the monitor when it has passed lock() and executes a method
      • Call unlock() when method is left
        • Thread leaves the monitor

2.2. Monitor Origin

  • Monitors proposed by Hoare; 1974
  • Abstract data type with MX guarantee

    • Methods encapsulate local variables

      • Just like methods in Java classes
    • Thread enters monitor via method

      • Built-in MX: At most one thread in monitor
    • In addition: Methods for cooperation
      • cwait(x): Blocks calling thread until csignal(x)
        • Monitor free then
      • csignal(x): Starts at most one thread waiting for x
        • If existing; otherwise, nothing happens

3. MX with Monitors in Java

3.1. Monitors in Java: Overview

  • In Java, classes and objects come with built-in locks

    • Which are ignored by default
  • Keyword synchronized activates locks

    • Automatic locking of this object during execution of method

      • Automatic MX for method’s body
      • Useful if (large part of) body is a CS
    • E.g., for sample code from (Hailperin 2019) (for which you found races previously):

      public synchronized void sell() {
        if (seatsRemaining > 0) {
          dispenseTicket();
          seatsRemaining = seatsRemaining - 1;
        } else displaySorrySoldOut();
      }
      

3.1.1. Java, synchronized, this

  • Java basics, hopefully clear
    • Method sell() from previous slides invoked on some object, say theater
      • Each theater has its own attribute seatsRemaining
      • seatsRemaining is really this.seatsRemaining, which is the same as theater.seatsRemaining
        • Inside the method, the name theater is unknown, theater is the this object, which is used implicitly
  • Without synchronized, races arise when two threads invoke sell() on the same object theater
    • With synchronized, only one of the threads obtains the lock on theater, so races are prevented

3.1.2. Possible Sources of Confusion

  • With synchronized, locks for objects are activated

    • For synchronized methods, thread needs to acquire lock for this object
  • Methods cannot be locked

  • Individual attributes of the this object (e.g., seatsRemaining) are not locked

    • (Which is not a problem as object-orientation recommends to encapsulate attributes, i.e., they cannot be accessed directly but only through synchronized methods)

3.1.3. Self-Study Task

  1. Inspect and understand, compile, and run this sample program, which embeds the code to sell tickets, for which you found races previously.
  2. Change sell() to use the monitor concept, recompile, and run again. Observe the expected outcome.

(Nothing to submit here; maybe ask questions online.)

3.2. Java Monitors in Detail

  • Every Java object (and class) comes with

    • Monitor with lock (not activated by default)

      • Keyword synchronized activates lock
      • For method: public synchronized methodAsCS(...) {…}

        • First thread acquires lock for this object upon call (Class object for static methods)
        • Further threads get blocked
      • Or for block: synchronized (syncObj) {…}

        • Thread acquires lock for syncObj

4. Cooperation with Monitors in Java

4.1. Producer/Consumer problems

  • Classical synchronization problems

    • Producers produce data, to be consumed by consumers
    • Data in shared data structure, e.g., Java array
      • Synchronization for data structure necessary

    • One or more producers
      • Generate data, e.g., records, messages, tasks
      • Place data into buffer (shared resource)
        • Two buffer variants: unbounded or bounded
        • Producer blocks, if bounded buffer is full
    • One or more consumers
      • Consume data
        • Take data out of buffer
        • Consumer blocks, if buffer is empty

4.2. Ideas for Cooperation

  • Use waiting and signaling of monitors
  • Threads may work with different roles on shared data structures
    • E.g., producer/consumer problems on previous slide
  • Some may find that they cannot continue before others did their work
    • The former call wait() and hope for notify() by the latter
    • Cooperation (orthogonal to and not necessary for MX!)
      • Wait set mentioned above and explained subsequently

4.3. wait() and notify() in Java

  • Waiting via blocking

    • wait(): thread unlocks and leaves monitor, enters wait set
      • Thread enters state blocked
      • Called by thread that cannot continue (without work/help of another thread)
  • Notifications

    • notify()
      • Remove one thread from wait set (if such a thread exists)
        • Change its state from blocked to runnable
      • Called by thread whose work may help another thread to continue
    • notifyAll()
      • Remove all threads from wait set
        • Only one can lock and enter the monitor, of course
        • Only after the notifying thread has left the monitor, of course
        • Overhead (may be avoidable with appropriate synchronization objects)

4.4. Sample synchronized Java Method

// Based on Fig. 4.17 of [Hai17]
public synchronized void insert(Object o)
  throws InterruptedException
// Called by producer thread
{
  while(numOccupied == buffer.length)
      // block thread as buffer is full;
      // cooperation from consumer required to unblock
      wait();
  buffer[(firstOccupied + numOccupied) % buffer.length] = o;
  numOccupied++;
  // in case any retrieves are waiting for data, wake/unblock them
  notifyAll();
}

(Part of SynchronizedBoundedBuffer.java)

4.5. Comments on synchronized

  • Previous method in larger program: bb.zip
    • SynchronizedBoundedBuffer as shared resource
    • Different threads (Producer instances and Consumer instances) call synchronized methods on that bounded buffer
      • Before methods are executed, lock of buffer needs to be acquired
        • This enforces MX for methods insert() and retrieve()
      • In methods, threads call wait() on buffer if unable to continue
        • this object used implicitly as target of wait()
        • Thread enters wait set of buffer
        • Until notifyAll() on same buffer
      • Note that thread classes contain neither synchronized nor wait/notify

Bibliography

Hailperin, Max. 2019. Operating Systems and Middleware – Supporting Controlled Interaction. revised edition 1.3.1. https://gustavus.edu/mcs/max/os-book/.

License Information

Source files are available on GitLab (check out embedded submodules) under free licenses. Icons of custom controls are by @fontawesome, released under CC BY 4.0.

Except where otherwise noted, the work “MX in Java”, © 2017-2024 Jens Lechtenbörger, is published under the Creative Commons license CC BY-SA 4.0.