Chapter 5 Java中的锁(一)

5.1 LOCK接口

锁是用于控制多个线程访问共享资源的方式。一般来说,一个锁能够防止多个线程同一时间访问一个共享资源(但是有些锁可以允许多个线程并发的访问共享资源,比如读写锁)。

Lock接口提供了与synchronized关键字类似的功能,只是在使用时需要显示的获取和释放锁。虽然它缺少了(synchronized块或方法所提供的)隐式获取和释放锁的便捷性,但是却拥有了锁获取与释放的可操作性可中断的获取锁以及超时获取锁等synchronized关键字所不具备的同步特性。

Lock接口的使用示例:

Lock lock = new ReentrantLock();

lock.lock();

try{

}finally{

      lock.unlock();

}

Lock 接口所提供的的synchronized关键字不具备的功能:

(1)尝试非阻塞的获取锁。

(2)能被中断地获取锁:与synchronized不同,当获取到锁的线程被中断时,中断异常将被抛出,同时锁被释放。

(3)超时获取锁:如果在规定时间内无法获得锁,则及时返回。


5.2 队列同步器

队列同步器AbstractQueuedSynchronizer,使用来构建锁或者其他同步组件的基础框架。

同步器主要通过操作3个方法来对同步状态进行更改,getState(),setState(int newState),compareAndSet(int expected,intupdate)。这些方法能都保证状态的改变是安全的。

同步器是实现锁(或任何其他同步组件)的关键,通常在锁的实现中聚合同步器,利用同步器来实现锁的语义。同步器简化了锁的实现,屏蔽了同步状态管理,线程的排队,等待与唤醒等底层操作。

5.2.1 队列同步器的接口与示例

(1)队列同步器是基于模板方法模式的,也就是说,使用者需要继承同步器并重写指定的方法。

同步器可重写的方法如下

(1)protected boolean tryAcquire(int arg):独占式的获取同步状态,实现该方法需要判断同步状态是否符合预期,然后进行CAS设置同步状态。

(2)protected boolean tryRelease(int arg):独占式释放同步状态,等待获取同步状态的线程将有机会获取同步状态。

(3)protected boolean tryAcquireShared(int arg):共享式获取同步状态,返回大于0的值,表示获取成功,反之,获取失败。

(4)protected boolean tryReleaseShared(int arg):共享式释放同步状态

(5)protected boolean isHeldExclusively():是否被当前线程独占

同步器提供的模板方法:

(1)void acquire(int arg):独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回,否则,线程进入同步队列等待,该方法会调用重写的tryAcquire方法。

(2)void acquireInterruptibly(int arg):与acquire(int arg)相同,但是该方法能够响应中断,如果当前线程被中断,则该方法会抛出InterruptedException并返回。

(3)boolean tryAcquireNanos(int arg,long nanos):在acquireInterruptibly(int arg)的基础上增加了超时限制,如果当前线程在超时时间内没有获取到同步状态,那么将返回false,否则返回true。

(4)boolean release(int arg):独占式释放同步状态,该方法会在释放同步状态之后,将同步状态中第一个节点包含的线程唤醒。

(5)Collection<Thread> getQueuedThreads():获取等待在同步队列上的线程集合

同步器提供的方法主要分为3类:独占式获取和释放同步状态共享式获取和释放同步状态查询同步队列中的等待线程情况。


5.3 重入锁

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

synchronized关键字隐式的支持重进入。

(1)公平锁:如果在绝对时间上,先对锁进行请求的锁先被满足,那么这个锁就是公平的,反之,就是不公平的。公平的获取锁,也就是等待时间最长的线程最优先获得锁,也可以说锁的获取是顺序的。

(2)事实上,公平锁往往没有非公平锁的效率高(公平锁造成大量的线程切换开销),但是,公平锁能够减少“饥饿”发生的概率。

(3)非公平锁虽然可能造成线程饥饿,但极少的线程切换,保证了其更大的吞吐量。

ReentrantLock的实现:

(1)nonfairTryAcquire:判断当前线程是否为获取锁的线程,如果是,则将同步状态的值增加并返回true。

(2)tryRelease:如果该锁被获取了n次,那么前(n-1)次tryRelease方法必须返回flase,而只有同步状态最终被减为0的时候才能返回true。

(3)公平锁的tryAcquire与nonfairTryAcquire唯一的区别就是,判断条件多了hasQueuedPredecessors()方法,即加入了同步队列当前节点是否有前驱节点的判断,如果有的话,tryAcquire会返回false。

nonfairTryAcquire


fairTryAcquire
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 5.4 读写锁 之前提到的锁如Mutex和ReentrantLock都是排它锁,这些锁同一时刻都只允许一个线程进行...
    伊凡的一天阅读 577评论 0 1
  • Java并发编程的艺术笔记 1.并发编程的挑战[https://www.jianshu.com/p/8c9d063...
    一个秋阅读 562评论 0 0
  • 1.Lock接口 一般来说,一个锁能够防止多个线程同时访问共享资源(但有些锁可以允许多个线程并发的访问共享资源,比...
    加夕阅读 479评论 0 1
  • 主要内容有:Lock接口队列同步器重入锁读写锁LockSupport工具Condition接口 1.Lock接口 ...
    htkeepmoving阅读 279评论 0 1
  • 1.Lock 接口   锁是用来控制多个线程访问共享资源的方式,一般来说, 一个锁能够防止多个线程同时访问共享资源...
    ShayHe阅读 398评论 0 0