icode icode
首页
  • Android学习

    • 📁基础内容
    • 📺AndroidCore
    • 🎨Android-UI
    • 🏖️Components
    • 📊Fragment
    • 🔗网络操作
    • 🔏异步机制
    • 📦数据存储
    • 🗃️Gradle
  • 学习笔记

    • 『框架』笔记
    • 『Kotlin』笔记
    • 《Vue》笔记
    • 《Git》学习笔记
    • 『Bug踩坑记录』
  • ListView
  • RecyclerView
  • ViewPager
  • Java笔记

    • 🟠JavaSE
    • 🟢JavaWeb
    • 🔴JavaEE
    • ⚪JavaTopic
    • 🍳设计模式
  • 计算机基础

    • 📌计算机网络
    • 🔍数据结构
    • 📦数据库
    • 💻OS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 关于

    • 📫关于我
  • 收藏

    • 网站
    • 资源
    • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

iqqcode

保持对技术的探索实践与热爱
首页
  • Android学习

    • 📁基础内容
    • 📺AndroidCore
    • 🎨Android-UI
    • 🏖️Components
    • 📊Fragment
    • 🔗网络操作
    • 🔏异步机制
    • 📦数据存储
    • 🗃️Gradle
  • 学习笔记

    • 『框架』笔记
    • 『Kotlin』笔记
    • 《Vue》笔记
    • 《Git》学习笔记
    • 『Bug踩坑记录』
  • ListView
  • RecyclerView
  • ViewPager
  • Java笔记

    • 🟠JavaSE
    • 🟢JavaWeb
    • 🔴JavaEE
    • ⚪JavaTopic
    • 🍳设计模式
  • 计算机基础

    • 📌计算机网络
    • 🔍数据结构
    • 📦数据库
    • 💻OS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 关于

    • 📫关于我
  • 收藏

    • 网站
    • 资源
    • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • JavaSE

    • 面向对象

    • 常用API

    • 集合类

    • 多线程

      • Thread

      • JUC

        • JUC包下的常用工具类
        • JUC包下常用的辅助类
          • 1.CountDownLatch
          • 2. CyclicBarrier
          • 3. Semaphore
            • 概述
            • 使用场景
          • 【三者小结】
          • 4. ReadWriteLock
        • 阻塞队列,同步队列
        • Java内存模型JMM详解
        • volatile
        • CAS自旋
        • CAS与volatile无锁解决并发
        • AQS
        • 线程池
        • Future接口
    • JDK8新特性

    • IO流

    • JVM

  • JavaWeb

  • JavaEE

  • JavaTopic

  • 设计模式

  • 计算机基础

  • Java后端
  • JavaSE
  • 多线程
  • JUC
iqqcode
2021-06-17
目录

JUC包下常用的辅助类

辅助工具类

# 1.CountDownLatch

倒计时锁🔒:同步辅助类,通过它可以阻塞当前线程。实现一个线程或者多个线程一直等待,直到其他线程执行的操作完成。

使用一个给定的计数器进行初始化,该计数器的操作是原子操作,即同时只能有一个线程操作该计数器。

【原理】:多个线程协同执行任务,每个线程任务执行完,计数减一countDown()

  • 构造方法用来初始化等待计数值
  • await()用来等待计数器归零
  • countDown()让计数器减一

image-20201002113234905

20190815191955880

调用该类await()方法的线程会一直阻塞,直到其他线程调用该类的countDown()方法,使当前计数器的值变为0为止。每次调用该类的countDown()方法,当前计数器的值就会减1。当计数器的值减为0的时候,所有因调用await()方法而处于等待状态的线程就会继续往下执行。这种操作只能出现一次,因为该类中的计数器不能被重置。

如果需要一个可以重置计数次数的版本,可以考虑使用CyclicBarrier类。

CountDownLatch支持给定时间的等待,超过一定的时间不再等待,使用时只需要在countDown()方法中传入需要等待的时间即可。此时,countDown()方法的方法签名如下:

public boolean await(long timeout, TimeUnit unit)
1

# 使用场景

在某些业务场景中,程序执行需要等待某个条件完成后才能继续执行后续的操作。

典型的应用为并行计算:

当某个处理的运算量很大时,可以将该运算任务拆分成多个子任务,等待所有的子任务都完成之后,父任务再拿到所有子任务的运算结果进行汇总。

【e.g.】

我们使用只有2个核心线程的线程池,分别执行查询订单和派送订单的操作,并且初始化了一个大小为 2 的 CountDownLatch,每次查询完后,都要 countDown();

主线程则一直等待减为 0 了之后,才开始继续往下执行。


# 2. CyclicBarrier

# 概述

是一个同步辅助类,允许一组线程相互等待,直到到达某个公共的屏障点,通过它可以完成多个线程之间相互等待,只有当每个线程都准备就绪后,才能各自继续往下执行后面的操作。

与CountDownLatch有相似的地方,都是使用计数器实现,当某个线程调用了CyclicBarrier的await()方法后,该线程就进入了等待状态,而且计数器执行加1操作,当计数器的值达到了设置的初始值,调用await()方法进入等待状态的线程会被唤醒,继续执行各自后续的操作。

CyclicBarrier在释放等待线程后可以重用,所以,CyclicBarrier又被称为循环屏障。

// 初始化值为5的栅栏
CyclicBarrier cyclicBarrier = new CyclicBarrier(5);

// 每个线程调用 await()
cyclicBarrier.await();

// 等到有 5 个线程都执行了 await() 之后,继续执行。

// 并且 栅栏的 计数器会自动重置为 5 ,可以接着用
1
2
3
4
5
6
7
8
9

# CyclicBarrier与CountDownLatch的区别

  • CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法进行重置,并且可以循环使用
  • CountDownLatch主要实现1个或n个线程需要等待其他线程完成某项操作之后,才能继续往下执行,描述的是1个或n个线程等待其他线程的关系。而CyclicBarrier主要实现了多个线程之间相互等待,直到所有的线程都满足了条件之后,才能继续执行后续的操作,描述的是各个线程内部相互等待的关系。
  • CyclicBarrier能够处理更复杂的场景,如果计算发生错误,可以重置计数器让线程重新执行一次。

【e.g.】

在英雄联盟中,选好英雄之后,会等待所有 10 个玩家进度条都到 100% 才开始游戏,我们可以使用 CyclicBarrier 来模拟这个场景


# 3. Semaphore

# 概述

Semaphore信号量: 用来限制访问共享资源的线程数,控制同一时间并发线程的数目

能够完成对于信号量的控制,可以控制某个资源可被同时访问的个数。

  • 每次只允许规定数量的线程来获取资源
  • 待规定的线程执行完释放了信号量,再调度规定数量的线程继续执行

提供了两个核心方法——acquire()方法和release()方法。

  • acquire():获取一个许可,如果没有则等待
  • release():在操作完成后释放对应的许可

Semaphore维护了当前访问的个数,通过提供同步机制来控制同时访问的个数

// 1. 创建Semaphore对象,允许三个线程同时获取资源
Semaphore semaphore = new Semaphore(3);

// 2. 10个线程同时运行
for (int i = 0; i < 10; i++) {
    new Thread(() -> {
        try {
            //获取信号量
            semaphore.acquire();
            
            //执行线程任务
        } catch () {
            
        } finally {
            //执行完,释放信号量
            semaphore.release();
        }
    }).start();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 使用场景

使用Semaphore限流,在访问高峰期时,让请求线程阻塞,高峰期过去再释放许可,当然它只适合限制单机线程数量(不适合分布式),并且仅是限制线程数,而不是限制资源数(例如客户端连接数,请对比Tomcat LimitLatch的实现)

适合用在资源数与线程数相同时使用,Semaphore常用于仅能提供有限访问的资源,比如:数据库连接数


# 【三者小结】

  1. CountDownLatch 可以实现计数等待,主要用于A线程等待其他几个线程执行完后再执行;

  2. CyclicBarrier 实现循环栅栏,主要用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

  3. CountDownLatch 是不能够重用的,而 CyclicBarrier 是可以重用的

  4. Semaphore 信号量,主要强调只有某些个数量的线程能拿到资源执行。

    Semaphore 其实和锁有点类似,它一般用于控制对 某组 资源的访问权限,而锁是控制对 某个 资源的访问权限

# 4. ReadWriteLock

独占锁(写锁):一次只能被一个线程占有

共享锁(读锁):多个线程可以同时占有

  • 读<---->读:可以共存
  • 读<---->写:互斥,不可共存
  • 写<---->写:互斥,不可共存
class MyCache {
    private  volatile Map<String,Object> map = new HashMap<> ();
    //读写锁,更加细粒度控制
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private Lock lock = new ReentrantLock();

    public void write(String key,Object value) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " writing " + value);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName() + " write over!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    public void read(String key) {
        readWriteLock.writeLock().lock();
        try {
            key = Thread.currentThread().getName();
            System.out.println(key + " reading ");
            map.get(key);
            System.out.println(key + " read over");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

}

public class ReadWriteLockTest {
    public static void main(String[] args) {
        MyCache cache = new MyCache();
        //写入操作
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(() -> {
                cache.write(Thread.currentThread().getName(), temp);
            },String.valueOf(i)).start();
        }

        //读取操作
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(() -> {
                cache.read(Thread.currentThread().getName());
            },String.valueOf(i)).start();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

加上ReadWriteLock之后,能够更加细粒度的控制锁,避免读写同时发生造成“脏读”。

编辑 (opens new window)
上次更新: 2021/06/27, 10:49:09
JUC包下的常用工具类
阻塞队列,同步队列

← JUC包下的常用工具类 阻塞队列,同步队列→

最近更新
01
匿名内部类
10-08
02
函数式接口
10-08
03
ARouter-Kotlin踩坑
10-05
更多文章>
Theme by Vdoing | Copyright © 2021-2023 iqqcode | MIT License | 备案号-京ICP备2021028793号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×