【Java核心基础知识】07 - 多线程并发(6)

多线程知识点目录

多线程并发(1)- https://www.jianshu.com/p/8fcfcac74033
多线程并发(2)-https://www.jianshu.com/p/a0c5095ad103
多线程并发(3)-https://www.jianshu.com/p/c5c3bbd42c35
多线程并发(4)-https://www.jianshu.com/p/e45807a9853e
多线程并发(5)-https://www.jianshu.com/p/5217588d82ba
多线程并发(6)-https://www.jianshu.com/p/d7c888a9c03c

十九、CAS(比较并交换-乐观锁机制-锁自旋)

19.1 概念及特性

CAS(Compare And Swap/Set)比较并交换。
CAS算法的过程:包含3个参数CAS(V,E,N),分别为:

  • V表示要更新的变量(内存值)
  • E表示预期值(旧的)
  • N表示当前值(新的)

当且仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做,最后,CAS返回当前V的真实值。
CAS操作是抱着乐观的态度进行的(乐观锁),它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂起,仅被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS操作即使没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。

19.2 原子包 java.util.concurrent.atomic(锁自旋)

JDK1.5的原子包:java.util.concurrent.atomic这个包里面提供了一组原子类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实力包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他现成打断,而别的现成就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择另一个线程进入,这只是一种逻辑上的理解。

相对于synchronized这种阻塞算法,CAS是非阻塞算法的一种常见实现(它在多线程环境中无需使用阻塞锁,从而减少了线程的阻塞和上下文切换,进而提高了程序的性能。)。由于一般CPU切换时间比CPU指令集操作更长,所以J.U.C在性能上有了很大的提升。

19.3 ABA问题

CAS的ABA问题是指在并发编程中,使用CAS算法时可能会出现的一种问题。

ABA问题的原因是,CAS算法需要在操作值的时候,检查某地址的内容有没有发生变化(和旧值进行比较),如果没有发生变化则更新为新的值。但是,如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。即:这个线程在操作的时候,其他线程也在进行操作。

例如,有两个线程T1和T2,T1从内存地址X中取出A,这时另一个线程T2也从内存地址X中取出A,并且线程T2进行了一系列操作将值改变成B,写回主物理内存。然后线程T2又将内存地址为X的数据变为A,这个时候线程T1进行了CAS操作发现内存中仍然是A,这时线程T1进行CAS操作成功。尽管线程T1的CAS操作成功,但并不代表这个过程就没有问题,这就是ABA问题。

为了解决ABA问题,可以使用版本号。那么A→B→A就会变成1A→2B→3A。具体来说,可以在每个值前面加上一个版本号,每次更新时将版本号加一。当进行CAS操作时,除了比较值之外,还需要比较版本号。只有当版本号不变时,才说明值没有发生变化。这种方法可以确保在并发环境下正确地更新值。

二十、AQS(抽象的队列同步器)

20.1 概念及特性

AbstractQueuedSynchronized类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReenTrantLock、Semaphore、CountDownLatch。

AbstractQueuedSynchronized底层结构

它维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。

这里的 volatile 是关键字,它确保了 state 的可见性。具体来说,volatile 关键字可以确保每个线程在读取 state 时都能得到最新的值,而不是读取到已经被其他线程修改过的旧值。

state 的访问方式主要有三种:getState()、setState() 和 compareAndSetState():

  1. getState():这个方法用于获取当前的状态值。
  2. setState():这个方法用于设置新的状态值。
  3. compareAndSetState():这个方法比较当前状态值和期望的状态值,如果两者相等,则设置新的状态值。这是一种原子操作,可以确保在多线程环境下,状态的改变是原子的。

当一个线程需要获取资源时,它首先会调用 getState() 方法来查看当前的状态。如果状态指示资源可用,那么线程就可以获取资源并进行操作。如果状态指示资源不可用,那么线程就会被放入等待队列中,直到状态变为可用。

当线程释放资源时,它会调用 setState() 方法来改变状态值。如果此时没有其他线程在等待该资源,那么状态改变后,等待队列中的线程就可以按顺序获取资源并进行操作。如果有其他线程在等待该资源,那么状态改变后,等待队列中的线程将再次被放入队列中等待。

20.2 AQS定义两种资源共享方式

Exclusive 独占资源(ReenTrantLock)

只有一个线程能执行,如 ReentrantLock。它又可分为公平锁和非公平锁:

  • 公平锁:按照线程在队列中的排队顺序,先到者先拿到锁。
  • 非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的。

资源共享和获取/释放的接口:tryAcquire() 和 tryRelease() 方法。当一个线程尝试获取资源时,如果资源当前不可用,那么该线程会被放入等待队列,直到资源可用。当线程释放资源时,会检查是否有其他线程在等待该资源。

Share共享资源(Semaphore/CountDownLatch)

多个线程可同时执行,如 CountDownLatch、Semaphore、CyclicBarrier、ReadWriteLock。例如,ReentrantReadWriteLock 可以看成是组合式,因为 ReentrantReadWriteLock 也就是读写锁允许多个线程同时对某一资源进行读。

资源共享和获取/释放的接口:tryAcquireShared() 和 tryReleaseShared() 方法。在共享模式下,多个线程可以同时获取资源,但通常会有一个限制,比如同时最多有几个线程可以获取资源。当一个线程尝试获取资源时,如果资源当前不可用,那么该线程会被放入等待队列,直到资源可用。当线程释放资源时,会唤醒等待队列中的一个线程。


由于独占模式和共享模式的接口方法不同,因此 AQS 本身并没有定义成抽象类,而是定义了一个接口。这样可以允许实现类根据具体的需求来实现不同的接口方法。

对于自定义同步器来说,只需要实现 state 的获取和释放方式即可。具体的线程等待队列的维护(如获取资源失败时的入队操作、唤醒出队等)已经由 AQS 在顶层实现了。这样可以让自定义同步器专注于实现具体的资源获取和释放策略,而不需要关心线程等待队列的维护细节。

20.3 同步器的实现

ReentrantLock为例,它的实现确实是基于 AQS(AbstractQueuedSynchronized)的,state 变量用来表示资源的状态。在 ReentrantLock 的 lock()方法中,会调用 tryAcquire()方法尝试获取资源,如果 state 为 0,表示资源未被占用,这时线程会将 state 加 1,并设置自己的状态为拥有资源状态。此后,其他线程再尝试 lock()时就会失败,直到当前拥有资源的线程调用 unlock()方法释放资源,将 state 设置为 0,其他线程才有机会获取该锁。

CountDownLatch的例子中,state 变量也用来表示资源的状态,但它的作用是记录需要等待的子线程数量。在 CountDownLatch 的构造函数中,会将 state 初始化为 N,表示有 N 个子线程需要执行。然后,这 N 个子线程会并行执行任务,每个子线程执行完后会调用 countDown()方法将 state 减 1。当 state 减为 0 时,表示所有子线程都已执行完毕,这时就会唤醒主调用线程,使其从 await()方法返回,继续后续操作。

无论是 ReentrantLock 还是 CountDownLatch,它们都利用了 AQS 提供的队列和 state 变量等核心机制来实现资源的获取和释放。通过这种方式,可以将同步器的实现与具体的资源获取和释放策略分离,从而提供更大的灵活性。

20.4 ReentrantReadWriteLock 实现独占和共享两种方式

ReentrantReadWriteLock 是 Java 中的一个读写锁,它允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。在 ReentrantReadWriteLock 中,读锁和写锁是互斥的,因此 ReentrantReadWriteLock 实现的是独占式的资源访问。

在 ReentrantReadWriteLock 中,读锁和写锁的获取和释放都是通过内部的锁来实现的。读锁和写锁的状态都是通过 state 变量来维护的。当一个线程获取写锁时,它会先将 state 加 1,表示有一个写线程正在等待获取锁。如果此时还有读线程正在等待获取读锁,那么这些读线程可以继续等待获取锁。但是,如果此时没有读线程正在等待获取读锁,那么获取写锁的线程就可以获取到锁并执行。

在实现 ReentrantReadWriteLock 时,需要同时实现独占和共享两种方式。具体来说,需要实现 tryAcquire()和 tryRelease()方法来支持独占式的资源访问,同时还需要实现 tryAcquireShared()和 tryReleaseShared()方法来支持共享式的资源访问。在 ReentrantReadWriteLock 中,获取读锁和写锁的过程是原子性的,因此可以实现精确的控制。

总之,ReentrantReadWriteLock 是一种支持独占和共享两种方式的同步器,它通过内部的锁来实现精确的控制,允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容