Java多线程--重入锁的实现原理

来源:互联网 发布:js数组去重系统方法 编辑:程序博客网 时间:2024/06/05 10:45

ReentrantLock 是一种支持支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁,除此之外,该锁还支持获取锁的公平性和非公平性选择。

1  重入的实现

对于锁的重入,我们来想这样一个场景。当一个递归方法被sychronized关键字修饰时,在调用方法时显然没有发生问题,执行线程获取了锁之后仍能连续多次地获得该锁,也就是说sychronized关键字支持锁的重入。对于ReentrantLock,虽然没有像sychronized那样隐式地支持重入,但在调用lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞。

如果想要实现锁的重入,至少要解决一下两个问题

  • 线程再次获取锁:锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取。
  • 锁的最终释放:线程重复n次获取了锁,随后在n次释放该锁后,其他线程能够获取该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数等于0时表示锁已经释放

 (1) 锁的获取

 下面来看看非公平锁的重入实现,它的实现在自定义同步器Sync内部nonfairTryAcquire方法
[java] view plain copy
  1. final boolean nonfairTryAcquire(int acquires) {  
  2.            final Thread current = Thread.currentThread();  
  3.            int c = getState();  
  4.            if (c == 0) {  
  5.      //CAS设置状态  
  6.                if (compareAndSetState(0, acquires)) {  
  7.                    setExclusiveOwnerThread(current);  
  8.                    return true;  
  9.                }  
  10.            }  
  11.     //检查当前线程 重入获取锁  
  12.            else if (current == getExclusiveOwnerThread()) {  
  13.                int nextc = c + acquires;  
  14.                if (nextc < 0// overflow  
  15.                    throw new Error("Maximum lock count exceeded");  
  16.                setState(nextc);//此时不需CAS  
  17.                return true;  
  18.            }  
  19.            return false;  
  20.        }  

该方法通过当前线程是否为获取锁的线程来决定获取操作是否成功,如果是获取锁的线程再次请求,则将同步状态值进行增加并返回true,表示获取同步状态成功。

(2)锁的释放

现在来看看锁的释放,它同样是定义在自定义同步器Sync内
[java] view plain copy
  1. protected final boolean tryRelease(int releases) {  
  2.             int c = getState() - releases;  
  3.             if (Thread.currentThread() != getExclusiveOwnerThread())  
  4.                 throw new IllegalMonitorStateException();  
  5.             boolean free = false;  
  6.             if (c == 0) {  
  7.                 free = true;  
  8.                 setExclusiveOwnerThread(null);  
  9.             }  
  10.             setState(c);  
  11.             return free;  
  12.         }  

如果该锁被获取了n次,那么前(n-1)次tryRelease方法必须返回false,而只有同步状态完全释放了,才能返回true。可以看出,该方法只有在同步状态为0的时候才会返回true,并将占有线程设置为null,表示释放成功。

2 公平锁的实现


所有公平性,就是在绝对时间上,先对锁进行获取的请求一定先被满足。也就是等待时间最长的线程最优先获取锁
公平锁的实现在同步器FairSync内
[java] view plain copy
  1. /** 
  2.          * Fair version of tryAcquire.  Don't grant access unless 
  3.          * recursive call or no waiters or is first. 
  4.          */  
  5.         protected final boolean tryAcquire(int acquires) {  
  6.             final Thread current = Thread.currentThread();  
  7.             int c = getState();  
  8.             if (c == 0) {  
  9.                 if (!hasQueuedPredecessors() &&//在此处加入判断逻辑  
  10.                     compareAndSetState(0, acquires)) {  
  11.                     setExclusiveOwnerThread(current);  
  12.                     return true;  
  13.                 }  
  14.             }  
  15.             else if (current == getExclusiveOwnerThread()) {  
  16.                 int nextc = c + acquires;  
  17.                 if (nextc < 0)  
  18.                     throw new Error("Maximum lock count exceeded");  
  19.                 setState(nextc);  
  20.                 return true;  
  21.             }  
  22.             return false;  
  23.         }  

该方法与非公平锁的获取唯一不同就是判断条件多了一个hasQueuedPredecessors方法,这个方法是在AbstractQueuedSynchronizer中定义
[java] view plain copy
  1. public final boolean hasQueuedPredecessors() {  
  2.         // The correctness of this depends on head being initialized  
  3.         // before tail and on head.next being accurate if the current  
  4.         // thread is first in queue.  
  5.         Node t = tail; // Read fields in reverse initialization order  
  6.         Node h = head;  
  7.         Node s;  
  8.         return h != t &&  
  9.             ((s = h.next) == null || s.thread != Thread.currentThread());  
  10.     }  

这个方法就是在判断同步队列中当前节点是否有前驱节点,如果有则返回true,则表示有线程比当前线程更早地请求获取锁,因此需要等待前驱线程获取并释放锁之后才能继续获取锁。 由此就实现了公平锁。
原创粉丝点击