Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: changed names/added other methods, reorganized with David

...

This noncompliant code example shows uses a thread-safe class Book. However, the class does not commit to its locking strategy and does not document that callers can safely use client-side locking. The class CheckOut uses client-side locking by synchronizing on a Book instance. that cannot be modified by a client.

Code Block

final 
Code Block
bgColor#FFCCCC

class Book {
  // May change its locking policy in the future to use private internal locks
  private final String title;
  private Calendar dateIssued;
  private Calendar dateDue;

  Book(String title, int days) {
    this.title = title; 
  }
  
  public synchronized void issueBook(int days) {
    dateIssued = Calendar.getInstance();
    dateDue = Calendar.getInstance();
    dateDue.add(Calendar.DAY_OF_MONTH, days);	 
  }

  public synchronized Calendar getDueDate() {
    return dateDue;
  }
}

Furthermore, the class does not commit to its locking strategy and does not document that callers can safely use client-side locking. The client class BookWrapper uses client-side locking in the renewBook() method by synchronizing on a Book instance.

Code Block
bgColor#FFCCCC
// Client
public class CheckOutBookWrapper {
  private final Book book;

  CheckOutBookWrapper(Book book) {
    this.book = book;
  }

  public void issueBook(int days) {
    book.issueBook();
  }

  public Calendar getDueDate() {
    return book.getDueDate();
  }

  public void renewBook() {
    synchronized(book) {
      if (book.getDueDate().after(Calendar.getInstance())) {
        throw new IllegalStateException("Book overdue");
      } else {
        book.issueBook(14);
      }
    }
  }
  // ...
}

If class Book changes its synchronization policy in the future, the CheckOut BookWrapper class's locking strategy will silently break. This is because threads that call getDueDate() of class CheckOut BookWrapper may perform operations on the thread-safe Book using its new locking policy (internal private lock), however, threads that call method renewBook() will always synchronize on the intrinsic lock of the Book instance. Consequently, the implementation will use two different locks.

...

Code Block
bgColor#ccccff
public class CheckOutBookWrapper {
  private final Book book;
  private final Object lock = new Object();

  CheckOutBookWrapper(Book book) {
    this.book = book;
  }

  public void issueBook(int days) {
    synchronized(lock) {
      book.issueBook();
    }
  }

  public Calendar getDueDate() {
    synchronized(lock) {
      return book.getDueDate();
    }
  }

  public void renewBook() {
    synchronized(lock) {
      if (book.getDueDate().after(Calendar.getInstance())) {
        throw new IllegalStateException("Book overdue");
      } else {
        book.issueBook(14);
      }
    }
  }
  // ...
}

Consequently, its locking strategy is independent of the locking policy of the Book instance.

...