Tac say

只想做个程序演奏家

读写锁-读可重入

发现这篇文献描写读写锁和可重入性非常具体。

尝试实现文章中的读可重入的读写锁:

考虑如何测试一个读可重入的读写锁, 由于读写锁的性质(可以同时存在多个读锁), 如果只是在线程A连续申请两个读锁, 就无法证明是锁的重入性发挥了作用。

测试思路是先在线程A申请读锁, 然后在线程B申请写锁, 若在线程C申请读锁, 此读锁应阻塞。而如果在线程A申请另一个读锁(前一个未释放), 线程A不应被阻塞。

测试代码(我的确用了Main来测试…)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public static void main(String[] args) throws InterruptedException {
  final ReadWriteLockReadReentrant lock = new ReadWriteLockReadReentrant();

  new Thread() {
      @Override
      public void run() {
          try {
              lock.lockRead();
              System.out.println("Reading");
              sleep(5000);
              System.out.println("Inner Reading");
              lock.lockRead();
          } catch (InterruptedException e) {

          } finally {
              System.out.println("Inner Reading End");
              lock.unlockRead();
              System.out.println("Reading End");
              lock.unlockRead();
          }
      }
  }.start();
  Thread.sleep(500);
  
  new Thread() {
      @Override
      public void run() {
          try {
              System.out.println("Request write lock");
              lock.lockWrite();
              System.out.println("Writing");
          } catch (InterruptedException e) {

          } finally {
              System.out.println("Writing End");
              lock.unlockWrite();
          }
      }
  }.start();
}

结果应当是:Reading(线程A),Request write lock(线程B),Inner Reading(未阻塞),Inner Reading End,Reading End,Writing,Writing End

读重入的读写锁(基本是抄上面文献的代码~)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import java.util.HashMap;
import java.util.Map;

public class ReadWriteLockReadReentrant {
    private boolean hasWriter = false;
    private int writeRequests = 0;
    private Map<Thread, Integer> readers = new HashMap<Thread, Integer>();

    public synchronized void lockRead() throws InterruptedException {
        Thread current = Thread.currentThread();
        while (!couldRead(current)) {
            wait();
        }
        readers.put(current, getReaders(current) + 1);
    }

    private int getReaders(Thread current) {
        if (!readers.containsKey(current)) {
            return 0;
        }
        return readers.get(current);
    }

    private boolean couldRead(Thread current) {
        if (hasWriter) {
            return false;
        }
        if (readers.containsKey(current)) {
            return true; //important
        }
        if (writeRequests > 0) {
            return false;
        }
        return true;
    }

    public synchronized void unlockRead() {
        Thread current = Thread.currentThread();
        setReaders(current, getReaders(current) - 1);
        notifyAll();
    }

    private void setReaders(Thread current, Integer now) {
        if (now == 0) {
            readers.remove(current);
        } else {
            readers.put(current, now);
        }
    }

    public synchronized void lockWrite() throws InterruptedException {
        writeRequests++;
        while (readers.size() > 0) {
            wait();
        }
        writeRequests--;
        hasWriter = true;
    }


    public synchronized void unlockWrite() {
        hasWriter = false;
        notifyAll();
    }
}

需要注意的是标记”//important”的那行,如果这个判断和writeRequest的判断互换位置,线程B申请写锁被阻塞时,线程A无法申请到第二个读锁。一切就悲剧了。

标记为”//important”的判断和writeRequest的判断互换位置
1
2
3
4
5
6
7
8
9
10
11
12
private boolean couldRead(Thread current) {
  if (hasWriter) {
      return false;
  }
  if (writeRequests > 0) {
      return false;
  }
  if (readers.containsKey(current)) {
      return true; //important
  }
  return true;
}

Comments