JAVA各种锁的优劣对比分析( 七 )

  • 如果当且写线程数为0(那么读线程也应该为0 , 因为上面已经处理c!=0的情况) , 并且当前线程需要阻塞那么就返回失败;如果通过CAS增加写线程数失败也返回失败 。
  • 如果c=0 , w=0或者c>0 , w>0(重入) , 则设置当前线程或锁的拥有者 , 返回成功!
  • tryAcquire()除了重入条件(当前线程为获取了写锁的线程)之外 , 增加了一个读锁是否存在的判断 。如果存在读锁 , 则写锁不能被获取 , 原因在于:必须确保写锁的操作对读锁可见 , 如果允许读锁在已被获取的情况下对写锁的获取 , 那么正在运行的其他读线程就无法感知到当前写线程的操作 。
    因此 , 只有等待其他读线程都释放了读锁 , 写锁才能被当前线程获取 , 而写锁一旦被获取 , 则其他读写线程的后续访问均被阻塞 。写锁的释放与ReentrantLock的释放过程基本类似 , 每次释放均减少写状态 , 当写状态为0时表示写锁已被释放 , 然后等待的读写线程才能够继续访问读写锁 , 同时前次写线程的修改对后续的读写线程可见 。
    接着是读锁的代码:
    JAVA各种锁的优劣对比分析

    文章插图
     
    可以看到在tryAcquireShared(int unused)方法中 , 如果其他线程已经获取了写锁 , 则当前线程获取读锁失败 , 进入等待状态 。如果当前线程获取了写锁或者写锁未被获取 , 则当前线程(线程安全 , 依靠CAS保证)增加读状态 , 成功获取读锁 。读锁的每次释放(线程安全的 , 可能有多个读线程同时释放读锁)均减少读状态 , 减少的值是“1<<16” 。所以读写锁才能实现读读的过程共享 , 而读写、写读、写写的过程互斥 。
    此时 , 我们再回头看一下互斥锁ReentrantLock中公平锁和非公平锁的加锁源码:
    JAVA各种锁的优劣对比分析

    文章插图
     
    我们发现在ReentrantLock虽然有公平锁和非公平锁两种 , 但是它们添加的都是独享锁 。根据源码所示 , 当某一个线程调用lock方法获取锁时 , 如果同步资源没有被其他线程锁住 , 那么当前线程在使用CAS更新state成功后就会成功抢占该资源 。而如果公共资源被占用且不是被当前线程占用 , 那么就会加锁失败 。所以可以确定ReentrantLock无论读操作还是写操作 , 添加的锁都是都是独享锁 。




    推荐阅读