JUC常用并发工具类

1,ReentrantLock

ReentrantLock是一种可重入的独占锁,它允许同一个线程多次获取同一个锁而不会被阻塞。

它的功能类似于synchronized是一种互斥锁,可以保证线程安全,相对于synchronized,

ReentrantLock具备如下特点:

  • 可中断
  • 可以设置超时时间
  • 可以设置为公平锁
  • 支持多个条件变量
  • 与synchronized一样,都支持可重入

它的主要应用场景是在多线程环境下对共享资源进行独占式访问,以保证数据的一致性和安全性

1.1 常用API

Lock接口

ReentrantLock实现了Lock接口规范,常见API如下

void lock()获取锁,调用该方法当前线程会获取锁,当锁获得后,该方法返回
void lockInterruptibly() throws InterruptedException可中断的获取锁,和lock()方法不同之处在于该方法会响应中断,即在锁的获取中可以中断当前线程
boolean tryLock()尝试非阻塞的获取锁,调用该方法后立即返回,如果能够获取到返回true,否则返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException

超时获取锁,当前线程在以下三种情况下会被返回:

当前线程在超时时间内获取了锁

当前线程在超时时间内被中断

超时时间结束,返回false

void unlock()释放锁
Condition newCondition()获取等待通知组件,该组件和当前的锁绑定,当前线程只有获取了锁,才能调用该组件的await()方法,而调用后,当前线程将释放锁

基本语法

//加锁,阻塞
lock.lock();
try{
    ....
}finally{
    // 解锁
    lock.unlock();
}

if (lock.tryLock(1, TimeUnit.SECONDS)){

    try {
        ....
    }finally {
        lock.unlock();
    }
}

在使用时要注意4个问题

1,默认情况下 ReentrantLock为非公平锁而非公平锁

2,加锁次数和释放锁次数一定要保持一致,否则会导致线程阻塞或程序异样

3,加锁操作一定要放在 try 代码之前,这样可以避免未加锁成功又释放锁的异常

4,释放锁一定要放在finally中,否则会导致线程阻塞

公平锁和非公平锁

ReentrantLock支持公平锁和非公平锁两种模式

公平锁:线程在获取锁时,按照等待的先后顺序获取锁

非公平锁:线程在获取锁时,不按照等待的先后顺序获取锁,而是随机获取锁。ReentrantLock默认是非公平锁

ReentrantLock lock = new ReentrantLock(); //参数默认false,不公平锁

ReentrantLock lock = new ReentrantLock(true); //公平锁

可重入锁

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁,在实际开发中,可重入锁常常应用于递归操作,调用同一个类中的其他方法,锁嵌套等场景

结合Condition实现生产者消费者模式

java.util.concurrent类库中提供Condition类来实现线程之间的协调。调用Condition.await()方法使线程等待,其他线程调用Condition.signal() 或 Condition.signalAll() 方法唤醒等待的线程

注意:调用Condition 的 await() 和 signal() 方法,都必须在lock保护之内

案例: 基于ReentrantLock 和 Condition 实现一个简单队列

1.3 应用场景总结

ReentrantLock具体应用场景如下

1.解决多线程竞争资源问题,例如多个线程同时对同一个数据库进行写操作,可以使用ReentrantLock保证每次只有一个线程能够写入

2,实现多多线程任务的顺序执行,例如在一个线程执行完某个任务后,再让另一个线程执行任务

3.实现多线程等待/通知机制,例如在某个线程执行完某个任务后,通知其他线程继续执行任务

2.Semaphore

Semaphore (信号量) 是一种用于多线程编程的同步工具,用于控制同时访问某个资源的线程数量。

 Semaphore维护了一个计数器,线程可以通过调用acquire()方法来获取Semaphore中的许可证,当计数器为0时,调用acquire()线程将被阻塞,直到有其他线程释放许可证,线程可以通过调用release()方法来释放Semaphore中的许可证,这会使Semaphore中的计数器增加,从而允许更多的线程访问共享资源

2.1 常用API

        构造器

        

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}

public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

permits 表示许可证的数量(资源数)

fair 表示公平性,如果这个设为true的话,下次执行的线程会是等待最久的线程

常用方法

acquire() 表示阻塞并获取许可

tryAcquire() 方法在没有许可的情况下会立即返回false,要获取许可的线程不会阻塞

release() 表示释放许可

2.3 应用场景总结

1,限流:Semaphore 可以用于限制对共享资源的并发访问的数量,以控制系统的流量

2,资源池:Semaphore 可以用于实现资源池,以维护一组有限的共享资源

3.CountDownLatch

CountDownLatch (闭锁)是一个同步协助类,允许一个或多个线程等待,直到其他线程完成操作集。

CountDownLatch使用给定的计数值(count) 初始化。await方法会阻塞直到当前的计数值(count) ,由于countDown方法的调用达到0,count为0之后所有等待的线程都会被释放,并且随后对await方法的调用都会立即返回。这是一个一次性现象,-----count 不会被重置

3.1 常用API

构造器

public CountDownLatch(int count){
    if (count < 0) throw new IllegalArgumentException("count < 0")
    this.sync = new Sync(count);
}

 常用方法

// 调用 await() 方法的线程会被挂起,它会等待直到 count 值为0 才继续执行
public void await() throws InterruptedException {}
// 和 await() 类似,若等待 timeout时长后,count的值还是没有变为0, 不再等待,继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException{}
// 会将 count 减1 直至为 0 
public void countDown(){}

3.3 应用场景总结

1,并行任务同步:CountDownLatch可以用于协调多个并行任务的完成情况,确保所有任务都完成后再继续执行下一步操作

2,多任务汇总:CountDownLatch 可以用于统计多个线程的完成情况,以确定所有线程都已完成工作

3,资源初始化:CountDownLatch 可以用于等待资源的初始化完成,以便在资源初始化完成后开始使用

4.CyclicBarrier

CyclicBarrier(回环栅栏或循环屏障),是Java并发库中的一个同步工具,通过它可以实现让一组线程等待只某个状态(屏障点)之后再全部同时执行,叫做回环是因为当所有等待线程都被释放后,CyclicBarrier可以被重用

4.1 常用API

构造器

// parties 表示屏障拦截的线程数量,每个线程调用await 方法告诉 CyclicBarrier 我已经到达屏障,然后当前线程被阻塞
public CyclicBarrier(int parties)
// 用于在线程到达屏障时,优先执行 barrierAction,方便处理更复杂的业务场景(该线程的执行时机是在到达屏障之后再执行)
public CyclicBarrier(int parties, Runnable barrierAction)

 

 常用方法

// 指定数量的线程全部调用await() 方法时,这些线程不再阻塞
// BrokenBarrierException 表示栅栏已经被破坏,破坏的原因可能是其中一个线程await()时被中断或者超时
public int await() throws InterruptedException, BrokenBarrierException
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException

// 循环 通过reset() 方法可以进行重置
public void reset()

4.3 应用场景总结

1,多线程任务:CyclicBarrier 可以用于将复杂的任务分配给多个线程执行,并在所有线程完成工作后出发后续操作

2,数据处理:CyclicBarrier 可以用于协调多个线程间的数据处理,在所有线程处理完数据后触发后续操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值