十一、JUC包中的锁

JUC,即java.util.concurrent.

悲观锁和乐观锁

悲观锁和乐观锁是一种思想。

悲观锁,持有一种悲观的态度,认为会出现很坏的情况,所以,先做预防措施。独占锁是一种悲观锁,synchronized就是一种独占锁。

而乐观锁,则是持有一种持有种乐观的态度,认为不会出现什么问题,有问题了再说。

对于常用多线程编程的人估计知道,在jdk5之前,在多线程编程的时候,为了保证多个线程对一个对象同时进行访问时,我们需要加同步锁synchronized,保证对象的在使用时的正确性,但是加锁的机制会导致如下几个问题

1.加多线程竞争下,加锁和释放锁会导致较多的上下文切换,引起性能问题。

2.多线程可以导致死锁的问题。

3.多线程持有的锁会导致其他需要此锁的线程挂起。

4......

CAS(Compare and Swap)无锁算法:英文全称翻译==比较与交换,是一种非阻塞无锁算法。既然是无所就不会出现线程阻塞。CAS的语义是“我认为V的值应该为A,如果是,那么将V的值更新为B,否则不修改并告诉V的值实际为多少”,CAS是项 乐观锁 技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

我曾经在一家做p2p系统的公司呆过,而系统很多部分基于版本记录机制实现的。即为数据增加一个版本标识,一般在表中添加一个version字段, 读取出数据时,将此版本号一同读出,之后更新时,对版本号进行比对,如果当前版本号与数据库一致,则对数据(version字段进行加1操作)进行更新,否则,认为数据过期,不予处理。

自旋锁:自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。

JUC包中的锁

相比同步锁,JUC包中的锁的功能更加强大,它为锁提供了一个框架,该框架允许更灵活地使用锁,只是它的用法更难罢了。

  JUC包中的锁,包括:Lock接 口,ReadWriteLock接口,LockSupport阻塞原语,Condition条 件,AbstractOwnableSynchronizer/AbstractQueuedSynchronizer /AbstractQueuedLongSynchronizer三个抽象类,ReentrantLock独占 锁,ReentrantReadWriteLock读写锁。由于CountDownLatch,CyclicBarrier和Semaphore也是通过 AQS来实现的;因此,我也将它们归纳到锁的框架中进行介绍。

  先看看锁的框架图,如下所示。

01. Lock接口

  JUC包中的 Lock 接口支持那些语义不同(重入、公平等)的锁规则。所谓语义不同,是指锁可是有"公平机制的锁"、"非公平机制的锁"、"可重入的锁"等等。"公平机制"是 指"不同线程获取锁的机制是公平的",而"非公平机制"则是指"不同线程获取锁的机制是非公平的","可重入的锁"是指同一个锁能够被一个线程多次获取。

02. ReadWriteLock

  ReadWriteLock 接口以和Lock类似的方式定义了一些读取者可以共享而写入者独占的锁。JUC包只有一个类实现了该接口,即 ReentrantReadWriteLock,因为它适用于大部分的标准用法上下文。但程序员可以创建自己的、适用于非标准要求的实现。

03. AbstractOwnableSynchronizer/AbstractQueuedSynchronizer/AbstractQueuedLongSynchronizer
  AbstractQueuedSynchronizer就是被称之为AQS
类,它是一个非常有用的超类,可用来定义锁以及依赖于排队阻塞线程的其他同步
器;ReentrantLock,ReentrantReadWriteLock,CountDownLatch,CyclicBarrier和
Semaphore等这些类都是基于AQS类实现的。AbstractQueuedLongSynchronizer
类提供相同的功能但扩展了对同步状态的 64 位的支持。两者都扩展了类
AbstractOwnableSynchronizer(一个帮助记录当前保持独占同步的线程的简单类)。

04. LockSupport
  LockSupport提供“创建锁”和“其他同步类的基本线程阻塞原语”。

  LockSupport的功能和"Thread中的Thread.suspend()和Thread.resume()有点类
似",LockSupport中的park() 和 unpark()
的作用分别是阻塞线程和解除阻塞线程。但是park()和unpark()不会遇到“Thread.suspend 和
Thread.resume所可能引发的死锁”问题。

05. Condition
  Condition需要和Lock联合使用,它的作用是代替Object监视器方法,可以通过await(),signal()来休眠/唤醒线程。
Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用 Object.wait
访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个 Lock 可能与多个 Condition
对象关联。为了避免兼容性问题,Condition 方法的名称与对应的 Object 版本中的不同。

  06. ReentrantLock

  ReentrantLock是独占锁。所谓独占锁,是指只能被独自占领,即同一个时间点只能被一个线程锁获取到的锁。ReentrantLock锁包
括"公平的ReentrantLock"和"非公平的ReentrantLock"。"公平的ReentrantLock"是指"不同线程获取锁的机制是
公平的",而"非公平的  ReentrantLock"则是指"不同线程获取锁的机制是非公平的",ReentrantLock是"可重入的锁"。
  ReentrantLock的UML类图如下:

  (01) ReentrantLock实现了Lock接口。
  (02) ReentrantLock中有一个成员变量sync,sync是Sync类型;Sync是一个抽象类,而且它继承于AQS。
  (03)
ReentrantLock中有"公平锁类"FairSync和"非公平锁类"NonfairSync,它们都是Sync的子类。
ReentrantReadWriteLock中sync对象,是FairSync与NonfairSync中的一种,这也意味着
ReentrantLock是"公平锁"或"非公平锁"中的一种,ReentrantLock默认是非公平锁。

07. ReentrantReadWriteLock
  ReentrantReadWriteLock是读写锁接口ReadWriteLock的实现类,它包括子类ReadLock和WriteLock。ReentrantLock是共享锁,而WriteLock是独占锁。
  ReentrantReadWriteLock的UML类图如下:


  (01) ReentrantReadWriteLock实现了ReadWriteLock接口。
  (02) ReentrantReadWriteLock中包含sync对象,读锁readerLock和写锁writerLock。读锁ReadLock和写锁WriteLock都实现了Lock接口。
  (03) 和"ReentrantLock"一样,sync是Sync类型;而且,Sync也是一个继承于AQS的抽象类。Sync也包括"公平锁"FairSync和"非公平锁"NonfairSync。

08. CountDownLatch
  CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
  CountDownLatch的UML类图如下:


  CountDownLatch包含了sync对象,sync是Sync类型。CountDownLatch的Sync是实例类,它继承于AQS。

09. CyclicBarrier
  CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
  CyclicBarrier的UML类图如下:


  CyclicBarrier是包含了"ReentrantLock对象lock"和"Condition对象trip",它是通过独占锁实现的。
  CyclicBarrier和CountDownLatch的区别是:
  (01) CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。
  (02) CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。

10. Semaphore
  Semaphore是一个计数信号量,它的本质是一个"共享锁"。
  信号量维护了一个信号量许可集。线程可以通过调用acquire()来获取信号量的许可;当信号量中有可用的许可时,线程能获取该许可;否则线程必须等待,直到有可用的许可为止。 线程可以通过release()来释放它所持有的信号量许可。
  Semaphore的UML类图如下:


  和"ReentrantLock"一样,Semaphore包含了sync对象,sync是Sync类型;而且,Sync也是一个继承于AQS的抽象类。Sync也包括"公平信号量"FairSync和"非公平信号量"NonfairSync。

------更多请参考Java多线程系列--“JUC锁”01之 框架

concurrent

时间: 2024-10-08 03:42:25

十一、JUC包中的锁的相关文章

JUC包中的锁框架

JUC包中的锁,包括:Lock接口,ReadWriteLock接口,LockSupport阻塞原语,Condition条件,AbstractOwnableSynchronizer/AbstractQueuedSynchronizer/AbstractQueuedLongSynchronizer三个抽象类,ReentrantLock独占锁,ReentrantReadWriteLock读写锁.由于CountDownLatch,CyclicBarrier和Semaphore也是通过AQS来实现的:因此

JUC包中的CountDownLatch源码实现分析

CountDownLatch是JUC包中提供的线程同步工具,使用CountDownLatch可以实现一个或多个线程等待直到一组操作在其他线程中被执行完成. CountDownLatch基于AQS实现,代码不多,所以很好分析.本文只分析CountDownLatch实现, 关于AQS的实现在另外一篇文章中叙述. 下面是CountDownLatch的类图: 接下来贴上来CountDownLatch的源码: public class CountDownLatch { private static fin

Java并发编程(3) JUC中的锁

一 前言 前面已经说到JUC中的锁主要是基于AQS实现,而AQS(AQS的内部结构 .AQS的设计与实现)在前面已经简单介绍过了.今天记录下JUC包下的锁是怎么基于AQS上实现的 二 同步锁 同步锁不是JUC中的锁但也顺便提下,它是由synchronized 关键字进行同步,实现对竞争资源互斥访问的锁. 同步锁的原理:对于每一个对象,有且仅有一个同步锁:不同的线程能共同访问该同步锁.在同一个时间点该同步锁能且只能被一个线程获取到,其他线程都得等待. 另外:synchronized是Java中的关

Java多线程 -- JUC包源码分析11 -- ThreadPoolExecutor源码分析

在JUC包中,线程池部分本身有很多组件,可以说是前面所分析的各种技术的一个综合应用.从本文开始,将综合前面的知识,逐个分析线程池的各个组件. -Executor/Executors -ThreadPoolExecutor使用介绍 -ThreadPoolExecutor实现原理 –ThreadPoolExecutor的中断与优雅关闭 shutdown + awaitTermination –shutdown的一个误区 Executor/Executors Executor是线程池框架最基本的几个接

JUC包实现的同步机制,原理以及简单用法总结

除了用Synchronized关键字修饰同步块,让线程获取某对象的锁实现多线程操作共享变量的同步外,还可以使用java.util.concurrent包.并且,juc包机制下的同步更灵活.juc包的所有锁都实现自Lock接口和ReadWriteLock接口,下面分别总结. (图片来源于网络) Lock接口 Lock在java.util.concurrent.locks包中,Lock是一个接口,我们一般使用的是它的实现类:ReentrantLock (可重入锁).ReentrantLock类的主要

Java多线程之JUC包:ReentrantReadWriteLock源码学习笔记

若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/go2sea/p/5634701.html ReentrantLock提供了标准的互斥操作,但在应用中,我们对一个资源的访问有两种方式:读和写,读操作一般不会影响数据的一致性问题.但如果我们使用ReentrantLock,则在需要在读操作的时候也独占锁,这会导致并发效率大大降低.JUC包提供了读写锁ReentrantReadWriteLock,使得读写锁分离,在上述情

Java多线程之JUC包:ReentrantLock源码学习笔记

若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/go2sea/p/5627539.html ReentrantLock是JUC包提供的一种可重入独占锁,它实现了Lock接口.与Semaphore类似,ReentrantLock也提供了两种工作模式:公平模式&非公平模式,也是通过自定义两种同步器FairSync&NonfairSync来实现的. 源代码: /* * ORACLE PROPRIETARY/CONF

【多线程】不懂什么是 Java 中的锁?看看这篇你就明白了!

本文来源:Java建设者 原文地址:https://mp.weixin.qq.com/s/GU42BjM5jY2CEMVD_PAZBQ Java 锁分类 Java 中的锁有很多,可以按照不同的功能.种类进行分类,下面是我对 Java 中一些常用锁的分类,包括一些基本的概述 从线程是否需要对资源加锁可以分为 悲观锁 和 乐观锁 从资源已被锁定,线程是否阻塞可以分为 自旋锁 从多个线程并发访问资源,也就是 Synchronized 可以分为 无锁.偏向锁. 轻量级锁和 重量级锁 从锁的公平性进行区分

JDK源码简析--java.lang包中的基础类库

题记 JDK,Java Development Kit. 我们必须先认识到,JDK只是,仅仅是一套Java基础类库而已,是Sun公司开发的基础类库,仅此而已,JDK本身和我们自行书写总结的类库,从技术含量来说,还是在一个层级上,它们都是需要被编译成字节码,在JRE中运行的,JDK编译后的结果就是jre/lib下得rt.jar,我们学习使用它的目的是加深对Java的理解,提高我们的Java编码水平. 本系列所有文章基于的JDK版本都是1.7.16. 本节内容 在本节中,简析java.lang包所包