• No se han encontrado resultados

PART I. THEORETICAL FRAMEWORK. AN INSIGHT INTO GENDER,

4. New patterns of masculinity in Middle-earth

4.3. Aragorn

4.3.1. Strider

Mark Wells, a former vice president of engineering at Agillion, suggested this antipattern. In the previous example, our synchronization scheme required us to lock the hash table objects for every hash table access. This lock is necessary so that the results of the execution are correct, even if there are multiple threads. Consider the following class:

class counter {

public static Integer count=0;

public void count() { Integer temp = count;

temp = temp + 1;

count = temp;

}

Table 5.3 shows a possible timeline for the program with two threads of exe-cution. Two threads are running the same program.

Antipattern: Synchronized Read/Write Bottlenecks 137

We can protect execution from simultaneous access by adding the synchro-nized keyword to the method:

public synchronized void count() {

For synchronized code, when a thread enters a method it locks the instance’s object so that other threads of the same instance cannot enter the method at the same time. Here’s the key: The Java synchronized keyword locks on the object level. For Listing 5.2, a synchronized method would not have been strong enough, because many commands could exist and write to the hash table at the same time. Therefore, we must lock the cache object.

5.5.1 Collisions between readers can hurt performance

For cache applications, you should have far more reads from the cache than writes to the cache. If this were not true, you’d be adding overhead without getting much value. You don’t need to protect simultaneous reads from the cache, because reads are not destructive. However, a writer and a reader using the cache at the same time would possibly generate unpredictable results. The problem is that writing applications need exclusive access, so to keep the read-ing applications out, both readers and writers must obtain a lock. Because the locks are exclusive, readers are also reduced to sequential access. This is a clas-sic case of the Read/Write Bottleneck antipattern.

Consider a bathroom that’s located between two important rooms of a house. It should be acceptable for many people to walk through the bath-room, as long as no one is using it. Once someone is using the bathbath-room, those passing through must clear out and the doors on either side must be

Table 5.3 Executing parallel threads can have many different results. This shows the possible results of executing our program with inadequate protection. While we should increment our counter twice, we come out with a total of one.

Value of count

Value

of temp Thread 1 Thread 2

0 0 Integer temp = count;

0 1 temp = temp + 1

0 0 Integer temp = count;

0 1 temp = temp + 1

1 1 count = temp

1? 1 count = temp

138 CHAPTER 5

Bitter cache management

locked. The readers share the bathroom, passing through concurrently. Writ-ers use the bathroom exclusively. We should not force people simply passing through to wait, but that’s precisely what our sample application does. We allow only one user of the bathroom, analogous to our cache objects, to pass through at any given time, regardless of the use. I’m reasonably certain that I have seen bathroom lines at many parties that indicated use of this algorithm.

If the read/write ratio is very high, then the penalty can be significant.

5.5.2 Read/write locks allow correct shared access

Database systems solve this problem with a multilevel locking system. In this case, obtaining a read lock on a database object allows multiple users to read the same data. A write lock is not compatible with a read lock. An application requesting a write lock must wait for all readers to clear. Java has no native support for read/write locks, but creating an object to provide this functional-ity is straightfor ward. To see how a read/write lock works, we present an example provided by Amandeep Singh, from a self-published article titled

“Implementing Read-Write Locks in Java”:

class RWLock {

private int givenLocks;

private int waitingWriters;

private int waitingReaders;

private Object mutex;

: :

public void getReadLock() {

synchronized(mutex) {

This method is used to request the read lock. It locks our common object, mutex, to control access to the internal variables. If writers are waiting or if writers have a lock, they’re allowed to clear before the lock is granted. given-Locks has a value of –1 when a writer has the lock:

Antipattern: Synchronized Read/Write Bottlenecks 139

public void getWriteLock() {

synchronized(mutex) {

waitingWriters++;

while(givenLocks != 0) {

mutex.wait();

}

waitingWriters--;

givenLocks = -1;

} }

When requesting a write lock, a thread signals that it is waiting by increment-ing the waitingWriters variable, waits until no more readers are holding the lock, and then takes a lock by setting givenLocks to –1 and decrements wait-ingWriters to signal that it is no longer waiting:

public void releaseLock();

{

synchronized(mutex) {

if(givenLocks == 0) return;

if(givenLocks == -1) givenLocks = 0;

else

givenLocks--;

mutex.notifyAll();

} } }

To release a lock, the protocol is followed in reverse. To use this lock, an appli-cation must:

Create a lock for each critical resource to be protected.

Request the lock for read before reading from the resource.

Release the lock for read after reading from the resource.

Request the lock for write before writing to the resource.

Release the lock for write after writing to the resource.

140 CHAPTER 5

Bitter cache management

The application will allow shared reads but will require exclusive writes. In our case, the application would have significantly higher throughput for most caching applications. Relational databases have proven the utility of robust read/write locks for years. Caching is an example where throughput can be sig-nificantly improved through the implementation of a read/write lock.

As always, a much better solution than rolling your own is to use a pre-packaged, respected utility. Many other resources exist for good concurrent programming in Java. Among the best is a book called Concurrent Program-ming in Java: Design Principles and Practices. In it, author Doug Lea describes locking considerations and other techniques for ensuring correctness. His website, http://g.oswego.edu/dl/, also includes util.concurrent, a well-respected collection of utilities for concurrent programming.