...
Code Block |
---|
|
// Client
public class BookWrapper {
private final Book book;
BookWrapper(Book book) {
this.book = book;
}
public void issue(int days) {
book.issue();
}
public Calendar getDueDate() {
return book.getDueDate();
}
public void renew() {
synchronized(book) {
if (book.getDueDate().after(Calendar.getInstance())) {
throw new IllegalStateException("Book overdue");
} else {
book.issue(14); // Issue book for 14 days
}
}
}
}
|
If class Book
changes its synchronization policy in the future, the BookWrapper
class's locking strategy might silently break. For instance, the Bookwrapper
class's locking strategy will definitely break if Book
is modified to use an internal private lock, as recommended by CON04-J. Synchronize using an internal private final lock object. This is because threads that call getDueDate()
of class BookWrapper
may perform operations on the thread-safe Book
using its new locking policy, however, threads that call method renew()
will always synchronize on the intrinsic lock of the Book
instance. Consequently, the implementation will use two different locks.
...
Code Block |
---|
|
public class BookWrapper {
private final Book book;
private final Object lock = new Object();
BookWrapper(Book book) {
this.book = book;
}
public void issue(int days) {
synchronized(lock) {
book.issue();
}
}
public Calendar getDueDate() {
synchronized(lock) {
return book.getDueDate();
}
}
public void renew() {
synchronized(lock) {
if (book.getDueDate().after( Calendar.getInstance())) {
throw new IllegalStateException("Book overdue");
} else {
book.issue(14); // Issue book for 14 days
}
}
}
}
|
Wiki Markup |
---|
Consequently, itsthe {{BookWrapper}} class's locking strategy is independent of the locking policy of the {{Book}} instance. This solution incurs a very small performance penalty but the resulting code is much more robust \[[Goetz 06|AA. Java References#Goetz 06]\]. |
...