慕课网高并发实战(七)- J.U.C之AQS

7.1  AbstractQueuedSynchronizer -AQS

底层实现了双向链表,是队列的一种实现方式

对象创建以后其状态就不能修改




底层是双向链表,队列的一种实现

sync queue:同步队列,head节点主要负责后面的调度

Condition queue:单向链表,不是必须的的,也可以有多个

设计原理

使用Node实现FIFO队列,可以用于构建锁或者其他同步装置的基础框架

利用了一个int类型标示状态,有一个state的成员变量,表示获取锁的线程数(0没有线程获取锁,1有线程获取锁,大于1表示重入锁的数量),和一个同步组件ReentrantLock,

使用方法是继承,基于模板方法

子类通过继承并通过实现它的方法管理其状态{acquire和release}的方法操作状态

可以实现排它锁和共享锁的模式(独占、共享,子类只能实现其中一个)

具体实现的思路

1.首先 AQS内部维护了一个CLH队列,来管理锁

线程尝试获取锁,如果获取失败,则将等待信息等包装成一个Node结点,加入到同步队列Sync queue里

3.不断重新尝试获取锁(当前结点为head的直接后继才会 尝试),如果获取失败,则会阻塞自己,直到被唤醒

4.当持有锁的线程释放锁的时候,会唤醒队列中的后继线程

AQS同步组件

CountDownLatch(闭锁,通过一个计数保证线程是否需要一直阻塞 )

Semaphore(控制同一时间并发线程的数目)

CyclicBarrier(与CountDownLatch 相识 阻塞线程,可以重置计数器)

ReentrantLock

Condition

FutureTask

CountDownLatch

同步阻塞类,可以完成阻塞线程的功能








*&&&& CountDownLatch :闭锁,通过一个计数,判断线程是否阻塞

&&&&  Semaphore:控制并发线程的数目


7.2  CountDownLatch 

同步辅组类,完成阻塞当前线程的功能,给定了一个计数器,原子操作,计数器不能重置。

调用await()方法会使线程处于阻塞状态,直到其他线程调用CountDown()方法时,才继续执行

当计数器变为0的时候,所有等待的线程才会继续执行

使用场景:查询需要等待某个条件完成后才能继续执行后续操作(Ex:并行计算)拆分任务



7.3 Semaphore


并发访问控制线程个数(同步机制),   提供了两个方法,实现有限大小的链表大小

semaphore.acquire(); // 获取一个许可

semaphore.release(); // 释放一个许可

semaphore.acquire(n);//获取多个许可

semaphore.release(n); // 释放n个许可


使用场景:仅能提供有限访问的资源,比如数据库连接数


tryAcquire())//尝试获取一个许可


tryAcquire 四个带参方法

 1 tryAcquire(long timeout, TimeUnit unit)

 2  tryAcquire()

3  tryAcquire(int permits)

4  boolean tryAcquire(int permits, long timeout, TimeUnit unit)


7.4  CyclicBarrier



运行一组线程等待到一个公共的屏障点,实现多个线程相互等待,当每一个线程都就绪后,才执行下去,通过计数器实现的

多线程计算数据,最后合并的场景


CyclicBarrier 与CountDownLatch 的区别

1  CountDownLatch 的计数器只能使用一次,CyclicBarrier 可以使用reset()方法重置

2 CountDownLatch 实现一个或者n个线程需要等待其他线程执行某项操作后才能继续执行 

 CyclicBarrier  实现多个线程了多个线程相互等待,知道多个线程都满足了某个条件以后才继续执行

描述的多个线程内部的关系,多个线程都调用await()方法后才继续向下执行

提供方法获取阻塞线程的个数,知道阻塞的线程是否中断

CyclicBarrier  对象调用await() 等待多个线程都满足条件后,在往下面执行


//定义有5个线程同步等待

1) private static CyclicBarrierbarrier =new CyclicBarrier(5);

2 )

在5个线程都满足条件后,先执行 log.info("callback is running"); 在执行以后的代码

private static CyclicBarrierbarrier =new CyclicBarrier(5, () -> {

log.info("callback is running");

});



7.5 ReentrantLock 与锁


java  两类锁: 1 synchronized 

                       2 JUC的 ReentrantLock 


 ReentrantLock 与synchronized 的区别

1 &&&  可重入性 两者都是可重入锁 ,同一线程进入一次 锁的计数器就自增1 ,锁的计数器下降为0 时才释放锁

2 &&&  synchronized 是依赖jvm实现的(操作系统实现,难查源码),ReentrantLock 是依赖jdk实现的(用户实现)

3 &&&  两者性能差不多 ,推荐使用synchronized ,synchronized 优化借鉴了CAS技术,用户态解决加锁问题避免进入内核态 使线程阻塞

4 &&&  synchronized  更方便它是编译器保证锁的加锁和释放的,ReentrantLock 手工释放和加锁,在finally释放锁

锁的细腻度和灵活度 ReentrantLock 更好 

ReentrantLock  的独有的功能

1  ReentrantLock 可指定是公平锁和非公平锁  synchronized 只能是非公平锁

公平锁(先等待的线程先获得锁)

2 提供了一个Condition类,可以分组唤醒需要唤醒的线程

synchronized  唤醒一个要不全部唤醒

3  提供能够中断等待锁的线程的机制,lock.lockInterruptibly()

ReentrantLock 实现是一种自旋锁,通过循环调用cas操作自加操作,避免了线程进入内核态发生阻塞 

synchronized 不会忘记释放锁

ReentrantLock 函数方法

tryLock():仅在调用时锁定未被另外一个线程保持的情况下获取锁定

tryLock(long timeout, TimeUnit unit) 如果锁定在给定的时间内没有被另一个线程保持,且当前线程没有被中断,则获取这个锁定

lockInterruptibly() 当前线程如果没有中断就获取锁定,如果已经中断就抛出异常

isLocked() 查询当前此锁定是否由任意线程保持,


ReentrantReadWriteLock   

 在没有任何 读写锁(ReadWrite)*的情况下才能取得写锁(Write)


StampedLock 

版本和模式两个部分组成

控制锁的三种方式:

1 写

2 读

3 乐观读

锁获取的方法是一个数字,用锁的状态控制相关锁的状态的访问

数字0 表示没有写锁

读锁分为 悲观锁 和乐观锁

对吞吐量有巨大的改进,特别是读线程多的场景中下

StampedLock  对于加锁容易误用其他的方法


Condition 


package com.mmall.concurrency.example.lock;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

@Slf4j

public class LockExample6 {

public static void main(String[] args) {

ReentrantLock reentrantLock =new ReentrantLock();

        Condition condition = reentrantLock.newCondition();

        new Thread(() -> {

try {

reentrantLock.lock(); // 线程加入到AQS的等待队列中

                log.info("wait signal"); // 1

                condition.await(); //调用await()方法后 线程从AQS对列中溢出(锁的释放),进入到condition的等待队列中

            }catch (InterruptedException e) {

e.printStackTrace();

            }

log.info("get signal"); // 4

            reentrantLock.unlock();

        }).start();

        new Thread(() -> {

reentrantLock.lock();

            log.info("get lock"); // 2

            try {

Thread.sleep(3000);

            }catch (InterruptedException e) {

e.printStackTrace();

            }

condition.signalAll();

            log.info("send signal ~ "); // 3

            reentrantLock.unlock();

        }).start();

    }

}

result:

- wait signal

- get lock

- send signal ~

- get signal




总结

1 只有少量竞争者的时候,synchronized是比较好的选择 

2 竞争者不少,线程的数量可以预估的,ReentrantLock 是一个比较好的锁实现

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

推荐阅读更多精彩内容

  • Java并发总结 1.多线程的优点 资源利用率更好 程序在某些情况下更简单 程序响应更快 2.创建线程 1.实现R...
    不会上树的猴子阅读 1,013评论 0 5
  • 1.明明就已经一大把年纪,却还是渴望童话世界,把自己当成公主,期待白马王子的出现。 2.明明才二十多岁,就开始每天...
    爱吃肉肉的小蜻蜓阅读 192评论 0 0
  • 关于AndroidAsync AndroidAsync封装了常用的异步请求比如获取字符串、获取JSON、获取文件等...
    黄海佳阅读 8,126评论 0 3
  • 2017高考作文诗歌 我拿着单反,骑着共享单车 今天是高考四十年,我的 高考在汽车火车自行车 的一带一路上预测 那...
    冰眉铁面阅读 377评论 0 3