关于多线程同步的初步教程--可重入锁的设计及使用

2014-11-23 23:22:35 · 作者: · 浏览: 0
上一篇中的Mutex是一个独占锁,只能有一个线程拥有该锁,并且即时是同一个线程,如果已经持有一个Mutex时,再次企图获取该锁时仍然会阻塞。有的时候我们需要锁能够像Java语言的synchronized那样,同一个线程可以重新进入,只要已经拥有了该锁,而不用在该锁上阻塞。我们可以对上篇中的Mutex的实现进行改造,实现一个可重入的锁--ReentrantLock。这需要ReentrantLock中记录当前锁的拥有者(线程),同时设置一个整型变量,记录当前线程进入的次数。

  1. public class ReentrantLock implements Sync {
  2. protected Thread owner_ = null;
  3. protected long holds_ = 0;
  4. //......
  5. }

在获取、释放锁时,首先判断该线程是否是锁的拥有者。如果是当前线程已经拥有该锁,则在每一次acquire()时增1,在release()时减1在次数减少到0时,说明该锁的当前拥有者已经完全释放该锁,不再拥有该锁。所以,将拥有者设置为null。如果当前线程不是锁的拥有者,那么在企图获取锁时在该锁上wait(),在release()方法中,如果拥有者已经完全释放锁,那么就将拥有者清零,并notify()其它线程。

  1. public void acquire() throws InterruptedException {
  2. if (Thread.interrupted()) throw new InterruptedException();
  3. Thread caller = Thread.currentThread();
  4. synchronized(this) { // 在this上同步
  5. if (caller == owner_)
  6. ++holds_;
  7. else {
  8. try {
  9. while (owner_ != null) wait();
  10. owner_ = caller;
  11. holds_ = 1;
  12. }
  13. catch (InterruptedException ex) {
  14. notify();
  15. throw ex;
  16. }
  17. }
  18. }
  19. }
  20. public synchronized void release() { //在this上同步
  21. if (Thread.currentThread() != owner_)
  22. throw new Error("Illegal Lock usage");
  23. if (--holds_ == 0) {
  24. owner_ = null;
  25. notify();
  26. }
  27. }


注意上面的代码要对owner_和holds_在this上进行同步,以解决在这两个变量上的竞态条件。attempt()方法实现和Mutex类似,也添加了锁拥有者的检查及计数:

  1. public boolean attempt(long msecs) throws InterruptedException {
  2. if (Thread.interrupted()) throw new InterruptedException();
  3. Thread caller = Thread.currentThread();
  4. synchronized(this) {
  5. if (caller == owner_) {
  6. ++holds_;
  7. return true;
  8. }
  9. else if (owner_ == null) {
  10. owner_ = caller;
  11. holds_ = 1;
  12. return true;
  13. }
  14. else if (msecs <=&nb