1 重入锁的好搭档:Condition条件

如果大家理解了Object.wait()Object.notify()方法的话,那么就能很容易理解Condition接口了。它和wait()notify()方法的作用大致是相同的。我们通过重入锁Lock接口的new Condition()方法可以生成一个与当前重入锁绑定的Condition实例。利用Condition对象,我们就可以让线程在合适的时间等待,或者在某一特定时刻得到通知,继续执行。顺带一提,Condition接口的实现类是ConditionObject

Condition接口的基本方法如下:

public interface Condition {
void await() throws InterruptedException;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();
}

以上方法的含义如下:

  • await():该方法会使当前线程在Condition队列(等待池)中等待,同时释放当前锁。当其他线程中使用signal()或者signalAll()方法时,线程会被唤醒并且加入到锁池中,准备竞争锁;或者当线程被中断时,也能跳出等待。
  • awaitUninterruptibly(),该方法与await()基本相同,只不过不会在等待过程中响应中断。
  • signal()方法用于唤醒一个处于等待池的线程,而signalAll()会唤醒所有处于等待池的线程。

2 允许多个线程同时访问:信号量(Semaphore)

信号量为多线程写作提供了更为强大的控制方法。无论是内部锁synchronized还是重入锁ReentrantLock,一次都只允许一个线程访问一个资源,而信号量却可以指定多个线程同时访问某一个资源。信号量主要提供了以下构造函数:

public Semaphore(int permits);
public Semaphore(int permits, boolean fair);

在构造信号量对象时,必须要指定信号量的准入数,即同时能申请多少个许可。当每个线程每次只申请一个许可时,这就相当于指定了同时有多少个线程可以访问某一个资源。信号量的主要逻辑方法有:

public void acquire();
public void acquireUninterruptibly();
public boolean tryAquire();
public boolean tryAquire(long timeout, TimeUnit unit);
public void release();

acquire()方法尝试获得一个准入的许可,若无法获得,则线程会等待,直到有线程释放许可或者当前线程被中断。acquireUninterruptibly()方法和acquire()方法类似,但是不相应中断。tryAcquire()尝试获得一个许可,如果成功则返回true,失败则返回false,它不会进行等待,立即返回。release()用于在线程访问资源结束后,释放一个许可,以使其他等待许可的线程可以进行资源访问。

3 倒计时器:CountDownLatch

CountDown在英文中意为倒计数,Latch为门闩的意思。在这里,门闩的含义是:把门锁起来,不让里面的线程跑出来,因此这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再开始执行。

CountDownLatch的构造函数接收一个整数作为参数,即当前这个计数器的计数个数。请看下面的示例:

/**
* @author mutianjie
* @date 2020/5/15 10:22 上午
* 对于火箭发射,为了保证万无一失,往往要进行各种检查,等所有设备检查完毕后才能点燃引擎。
*/
public class CountDownLatchDemo implements Runnable{
static final CountDownLatch end = new CountDownLatch(20);
static final CountDownLatchDemo demo = new CountDownLatchDemo();
@Override
public void run() {
try {
// 模拟检查任务
Thread.sleep(new Random().nextInt(10) * 1000);
System.out.println("Check Complete!");
end.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void main(String[] args) throws InterruptedException {
ExecutorService exec = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
exec.submit(demo);
}
// 等待检查
// 官方Javadoc:Causes the current thread to wait until the latch has counted down to zero,
// unless the thread is interrupted.
end.await();
// 发射火箭
System.out.println("Fire!");
exec.shutdown();
}
}

主线程执行end.await()等待,直到计数器减为0之后才继续执行。

4 循环栅栏:CyclicBarrier

CyclicBarrier是另外一种多线程并发控制工具。CyclicBarrier可以理解为循环栅栏。栅栏就是一种障碍物,比如,通常在私人宅邸的周围就可以围上一圈栅栏,阻止闲杂人等入内。这里当然是用来阻止线程继续执行,要求线程在栅栏处等待。而这里循环的意思,也就是说这个计数器可以循环使用。比如,我们假设将计数器设置为10,那么凑齐第一批10个线程后,计数器就会归零,然后接着凑齐下一批10个线程。

CyclicBarrier的构造函数除了接收计数总数parties,还可以接收一个参数Runnable barrierAction,这个参数表示当计数器完成一轮计数之后,系统会执行的动作。

public CyclicBarrier(int parties, Runnable barrierAction)

举个例子:

public class CyclicBarrierDemo {

    static class TaskThread extends Thread {

        CyclicBarrier barrier;

        public TaskThread(CyclicBarrier barrier) {
this.barrier = barrier;
} @Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(getName() + " 到达栅栏 A");
barrier.await();
System.out.println(getName() + " 冲破栅栏 A"); Thread.sleep(2000);
System.out.println(getName() + " 到达栅栏 B");
barrier.await();
System.out.println(getName() + " 冲破栅栏 B");
} catch (Exception e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
int threadNum = 5;
CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() { @Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 完成最后任务");
}
}); for(int i = 0; i < threadNum; i++) {
new TaskThread(barrier).start();
}
} }

打印结果:

Thread-1 到达栅栏 A
Thread-3 到达栅栏 A
Thread-0 到达栅栏 A
Thread-4 到达栅栏 A
Thread-2 到达栅栏 A
Thread-2 完成最后任务
Thread-2 冲破栅栏 A
Thread-1 冲破栅栏 A
Thread-3 冲破栅栏 A
Thread-4 冲破栅栏 A
Thread-0 冲破栅栏 A
Thread-4 到达栅栏 B
Thread-0 到达栅栏 B
Thread-3 到达栅栏 B
Thread-2 到达栅栏 B
Thread-1 到达栅栏 B
Thread-1 完成最后任务
Thread-1 冲破栅栏 B
Thread-0 冲破栅栏 B
Thread-4 冲破栅栏 B
Thread-2 冲破栅栏 B
Thread-3 冲破栅栏 B

线程间配合:Condition、Semaphore、CountDownLatch、CyclicBarrier的更多相关文章

  1. 线程间同步之 semaphore(信号量)

    原文地址:http://www.cnblogs.com/yuqilin/archive/2011/10/16/2214429.html semaphore 可用于进程间同步也可用于同一个进程间的线程同 ...

  2. JDK5.0 特性-线程同步装置之Semaphore

    来自:http://www.cnblogs.com/taven/archive/2011/12/17/2291474.html import java.util.ArrayList; import j ...

  3. Java多线程编程(5)--线程间通信

    一.等待与通知   某些情况下,程序要执行的操作需要满足一定的条件(下文统一将其称之为保护条件)才能执行.在单线程编程中,我们可以使用轮询的方式来实现,即频繁地判断是否满足保护条件,若不满足则继续判断 ...

  4. Java 如何实现线程间通信?(notify、join、CountdownLatch、CyclicBarrier、FutureTask、Callable )

    转自:https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247486499&idx=1&sn=d3f2d6959df ...

  5. 等待某(N)个线程执行完再执行某个线程的几种方法(Thread.join(),CountDownLatch,CyclicBarrier,Semaphore)

    1.main线程中先调用threadA.join() ,再调用threadB.join()实现A->B->main线程的执行顺序 调用threadA.join()时,main线程会挂起,等 ...

  6. 并发包下常见的同步工具类详解(CountDownLatch,CyclicBarrier,Semaphore)

    目录 1. 前言 2. 闭锁CountDownLatch 2.1 CountDownLatch功能简介 2.2 使用CountDownLatch 2.3 CountDownLatch原理浅析 3.循环 ...

  7. Java中的4个并发工具类 CountDownLatch CyclicBarrier Semaphore Exchanger

    在 java.util.concurrent 包中提供了 4 个有用的并发工具类 CountDownLatch 允许一个或多个线程等待其他线程完成操作,课题点 Thread 类的 join() 方法 ...

  8. AQS之CountDownLatch、Semaphore、CyclicBarrier

    CountDownLatch A synchronization aid that allows one or more threads to wait until a set of operatio ...

  9. CountDownLatch/CyclicBarrier/Semaphore 使用过吗?

    CountDownLatch/CyclicBarrier/Semaphore 使用过吗?下面详细介绍用法: 一,(等待多线程完成的)CountDownLatch  背景; countDownLatch ...

随机推荐

  1. Linux,Mac下MySQL的安装及一些知识点的整理

    Linux下载安装 在服务器上下载的话,需要安装Mysql5.7相关的yum源 wget https://dev.mysql.com/get/mysql80-community-release-el7 ...

  2. [JavaWeb基础] 013.Struts2 自定义类型转换器

    很多时候,我们在做web开发的时候经常要用到时间的转换器,Struts2给我们提供了一种类型转换器的接口.下面我们讲讲怎么实现吧. 1.首先我们要定义一个类型转换的类,继承自com.babybus.s ...

  3. Mysql面试的技术名词

    面试的技术名词 面试一般会遇到一些名词,其实可能自己都知道其中的道理,但是因为没了解过,当时心里就一句WC,然后弱弱答一句:不好意思这个我只是听过,具体还没了解过: 回表 覆盖索引 最左前缀匹配 索引 ...

  4. Java中方法的重载与重写

    1.方法的名字和参数列表称为方法的签名:每个方法具有唯一与其对应的签名: 2.方法的重载:在某个类中,存在具有多个相同名字不同参数列表的方法,称之为重载: 被重载的方法必须改变参数列表(参数个数或类型 ...

  5. Rocket - subsystem - CrossingWrapper

    https://mp.weixin.qq.com/s/3-MfNJDCIgOBqUbf4fuerQ 简单介绍CrossingWrapper的实现. 1. CrossesToOnlyOneClockDo ...

  6. Java实现 LeetCode 273 整数转换英文表示

    273. 整数转换英文表示 将非负整数转换为其对应的英文表示.可以保证给定输入小于 231 - 1 . 示例 1: 输入: 123 输出: "One Hundred Twenty Three ...

  7. Java实现 LeetCode 227 基本计算器 II(二)

    227. 基本计算器 II 实现一个基本的计算器来计算一个简单的字符串表达式的值. 字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 . 整数除法仅保留整数部分. 示例 1: 输入: ...

  8. Java实现 LeetCode 154 寻找旋转排序数组中的最小值 II(二)

    154. 寻找旋转排序数组中的最小值 II 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). 请找 ...

  9. java代码(8) ---guava字符串工具

    guava字符串工具 一.Joiner 根据指定的分隔符把字符串连接在一起,MapJoiner执行相同的操作,但是针对Map的key和value 分析源码可知:该类构造方法被private修饰,无法直 ...

  10. Navicat 连接远程服务器端MySQL

    Navicat是一个很好的操作各种数据库的图形化工具,我用它在本地连接过MySQL.SQL Server.SQLite,用它操作数据库确实非常方便.快捷,再搭配SQL语句,是一个很好的选择了. 废话不 ...