`
klcwt
  • 浏览: 189289 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

Java Concurrency: Read / Write Locks

    博客分类:
  • java
阅读更多
Jakob has done a great series on Java Concurrency - check out the first 14 articles at his blog. Going forward, we're delighted to announce that you'll also be able to follow the series here on JavaLobby.

A read / write lock is more sophisticated lock than the Lock implementations shown in the text Locks in Java. Imagine you have an application that reads and writes some resource, but writing it is not done as much as reading it is. Two threads reading the same resource does not cause problems for each other, so multiple threads that want to read the resource are granted access at the same time, overlapping. But, if a single thread wants to write to the resource, no other reads nor writes must be in progress at the same time. To solve this problem of allowing multiple readers but only one writer, you will need a read / write lock.

Java 5 comes with read / write lock implementations in the java.util.concurrent package. Even so, it may still be useful to know the theory behind their implementation.

Here is a list of the topics covered in this text:

Read / Write Lock Java Implementation
Read / Write Lock Reentrance
Read Reentrance
Write Reentrance
Read to Write Reentrance
Write to Read Reentrance
Fully Reentrant Java Implementation
Calling unlock() from a finally-clause


Read / Write Lock Java Implementation
First let's summarize the conditions for getting read and write access to the resource:

Read Access  If no threads are writing, and no threads have requested write access.
Write Access  If no threads are reading or writing.

If a thread wants to read the resource, it is okay as long as no threads are writing to it, and no threads have requested write access to the resource. By up-prioritizing write-access requests we assume that write requests are more important than read-requests. Besides, if reads are what happens most often, and we did not up-prioritize writes, starvation could occur. Threads requesting write access would be blocked until all readers had unlocked the ReadWriteLock. If new threads were constantly granted read access the thread waiting for write access would remain blocked indefinately, resulting in starvation. Therefore a thread can only be granted read access if no thread has currently locked the ReadWriteLock for writing, or requested it locked for writing.

A thread that wants write access to the resource can be granted so when no threads are reading nor writing to the resource. It doesn't matter how many threads have requested write access or in what sequence, unless you want to guarantee fairness between threads requesting write access.

With these simple rules in mind we can implement a ReadWriteLock as shown below:

view sourceprint?01.public class ReadWriteLock{
02. 
private int readers       = 0;
03. 
private int writers       = 0;
04. 
private int writeRequests = 0;
05. 
public synchronized void lockRead() throws InterruptedException{
06.   
while(writers > 0 || writeRequests > 0){
07.     
wait();
08.   
}
09.   
readers++;
10. 
}
11. 
public synchronized void unlockRead(){
12.   
readers--;
13.   
notifyAll();
14. 
}
15. 
public synchronized void lockWrite() throws InterruptedException{
16.   
writeRequests++;
17.   
while(readers > 0 || writers > 0){
18.     
wait();
19.   
}
20.   
writeRequests--;
21.   
writers++;
22. 
}
23. 
public synchronized void unlockWrite() throws InterruptedException{
24.   
writers--;
25.   
notifyAll();
26. 
}
27.}
The ReadWriteLock has two lock methods and two unlock methods. One lock and unlock method for read access and one lock and unlock for write access.

The rules for read access are implemented in the lockRead() method. All threads get read access unless there is a thread with write access, or one or more threads have requested write access.

The rules for write access are implemented in the lockWrite() method. A thread that wants write access starts out by requesting write access (writeRequests++). Then it will check if it can actually get write access. A thread can get write access if there are no threads with read access to the resource, and no threads with write access to the resource. How many threads have requested write access doesn't matter.

It is worth noting that both unlockRead() and unlockWrite() calls notifyAll() rather than notify(). To explain why that is, imagine the following situation:

Inside the ReadWriteLock there are threads waiting for read access, and threads waiting for write access. If a thread awakened by notify() was a read access thread, it would be put back to waiting because there are threads waiting for write access. However, none of the threads awaiting write access are awakened, so nothing more happens. No threads gain neither read nor write access. By calling noftifyAll() all waiting threads are awakened and check if they can get the desired access.

Calling notifyAll() also has another advantage. If multiple threads are waiting for read access and none for write access, and unlockWrite() is called, all threads waiting for read access are granted read access at once - not one by one.



Read / Write Lock Reentrance
The ReadWriteLock class shown earlier is not reentrant. If a thread that has write access requests it again, it will block because there is already one writer - itself. Furthermore, consider this case:

Thread 1 gets read access.
Thread 2 requests write access but is blocked because there is one reader.
Thread 1 re-requests read access (re-enters the lock), but is blocked because there is a write request
In this situation the previous ReadWriteLock would lock up - a situation similar to deadlock. No threads requesting neither read nor write access would be granted so.

To make the ReadWriteLock reentrant it is necessary to make a few changes. Reentrance for readers and writers will be dealt with separately.

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics