Wiki Markup |
---|
According to Goetz and colleagues \[[Goetz 2006|AA. Bibliography#Goetz 06]\]: |
Client-side locking entails guarding client code that uses some object X with the lock X uses to guard its own state. In order to use client-side locking, you must know what lock X uses.
...
Wiki Markup |
---|
The documentation of a class that supports client-side locking should explicitly state its applicability. For example, the class {{java.util.concurrent.ConcurrentHashMap<K,V>}} should not be used for client-side locking because its documentation (\[[API 2006|AA. Bibliography#API 06]\]) states that: |
... even though all operations are thread-safe, retrieval operations do not entail locking, and there is not any support for locking the entire table in a way that prevents all access. This class is fully interoperable with
Hashtable
in programs that rely on its thread safety but not on its synchronization details.
Wiki Markup |
---|
Use of client-side locking is permitted only when the documentation of the class recommends it. For example, the documentation of the {{synchronizedList()}} wrapper method of {{java.util.Collections}} class states \[[API 2006|AA. Bibliography#API 06]\] states |
In order to guarantee serial access, it is critical that all access to the backing list is accomplished through the returned list. It is imperative that the user manually synchronize on the returned list when iterating over it. Failure to follow this advice may result in non-deterministic behavior.
...
Code Block |
---|
final class Book {
// Could change its locking policy in the future
// to use private final locks
private final String title;
private Calendar dateIssued;
private Calendar dateDue;
Book(String title) {
this.title = title;
}
public synchronized void issue(int days) {
dateIssued = Calendar.getInstance();
dateDue = Calendar.getInstance();
dateDue.add(dateIssued.DATE, days);
}
public synchronized Calendar getDueDate() {
return dateDue;
}
}
|
This class fails to commit to its locking strategy (that is, it reserves the right to change its locking strategy without notice). Furthermore, it fails to document that callers can safely use client-side locking. The client class BookWrapper
client class uses client-side locking in the renew()
method by synchronizing on a Book
instance.
Code Block | ||
---|---|---|
| ||
// Client public class BookWrapper { private final Book book; BookWrapper(Book book) { this.book = book; } public void issue(int days) { book.issue(days); } public Calendar getDueDate() { return book.getDueDate(); } public void renew() { synchronized(book) { if (book.getDueDate().before(Calendar.getInstance())) { throw new IllegalStateException("Book overdue"); } else { book.issue(14); // Issue book for 14 days } } } } |
If the Book
class changes were to change its synchronization policy in the future, the BookWrapper
class's locking strategy might silently break. For instance, the Bookwrapper
BookWrapper
class's locking strategy would break if Book
were modified to use a private final lock object, as recommended by rule LCK00-J. Use private final lock objects to synchronize classes that may interact with untrusted code. The BookWrapper
class's locking strategy would break because threads that call BookWrapper.getDueDate()
may would perform operations on the thread-safe Book
using its new locking policy. However, threads that call the renew()
method would always synchronize on the intrinsic lock of the Book
instance. Consequently, the implementation would use two different locks.
...
Code Block | ||
---|---|---|
| ||
// This class could change its locking policy in the future, // for example, // if new non-atomic methods are added class IPAddressList { private final List<InetAddress> ips = Collections.synchronizedList(new ArrayList<InetAddress>()); public List<InetAddress> getList() { return ips; // No defensive copies required as package-private visibility } public void addIPAddress(InetAddress address) { ips.// as visibility is package-private } public void addIPAddress(InetAddress address) { ips.add(address); } } class PrintableIPAddressList extends IPAddressList { public void addAndPrintIPAddresses(InetAddress address) { synchronized (getList()) { addIPAddress(address); InetAddress[] ia = (InetAddress[]) getList().toArray(new InetAddress[0]); // ... } } } |
...
Code Block | ||
---|---|---|
| ||
// Class IPAddressList remains unchanged class PrintableIPAddressList { private final IPAddressList ips; public PrintableIPAddressList(IPAddressList list) { this.ips = list; } public synchronized void addIPAddress(InetAddress address) { ips.addIPAddress(address); } public synchronized void addAndPrintIPAddresses(InetAddress address) { addIPAddress(address); InetAddress[] ia = (InetAddress[]) ips.getList().toArray(new InetAddress[0]); // ... } } |
...
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="241a261aa88a4b6e-458def49-4f85499e-bb56a621-f5dbfd9b2c4ff3d4575a482c"><ac:plain-text-body><![CDATA[ | [[API 2006 | AA. Bibliography#API 06]] | Class | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="6d934109161673c8-fe83f5f4-4df7463f-9ff38539-a705c33d121f82836c6bbae3"><ac:plain-text-body><![CDATA[ | [[JavaThreads 2004 | AA. Bibliography#JavaThreads 04]] | 8.2 ", Synchronization and Collection Classes " | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="52fc2d2b675c0d5a-bd959f96-42f5439d-951bb6f1-a912853a0d8aa5154b466418"><ac:plain-text-body><![CDATA[ | [[Goetz 2006 | AA. Bibliography#Goetz 06]] | 4.4.1. , Client-side Locking, ; 4.4.2. , Composition; and 5.2.1. , | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="c0fa183bc5f57ae8-8606e643-4dff4333-8a01b8de-f681962dfaa49bf4ed5233f6"><ac:plain-text-body><![CDATA[ | [[Lee 2009 | AA. Bibliography#Lee 09]] | " Map & Compound Operation " | ]]></ac:plain-text-body></ac:structured-macro> |
...