之前看了CountDownLatch,他的实现是使用了aqs,提前设置好state的值,如果state不是0的时候调用await就会阻塞当前线程,加入到aqs的队列中,调用countDown就会减小state的值,当state的值时0的时候就会释放锁,将队列中的所有的线程释放,开始运行。这个CountDownLatch可以用于不同种类的线程之间,比如我们在连zk的时候,因为zkClient内部是使用的另一个线程链接zk,我们的主线程要在zk链接之后才能继续操作,所以就使用了CountDownLatch,这是用在不同种类的线程中。CountDownLatch也可以用在相同种类的线程中,比如有这么一个需求:要去一个饭店开一个生日宴会,这个宴会必须等待所有的朋友来了才可以一起开始,这个时候可以使用下面的代码:
public static void main(String[] args) { final CountDownLatch latch = new CountDownLatch(10); for(int i=0;i<10;i++){ new Thread(new Runnable() { public void run() { System.out.println(Thread.currentThread().getName() + " 已经来到了。");//一个线程来了,代表一个顾客 latch.countDown();//表示又来了一个人 try { latch.await();//当前的人等待其他人来 System.out.println("既然都来了,就开始吧");//当所有的人都来了,则开始 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }).start(); } }
上面的代码中,CountDownLatch是用在同种类的线程中,表示所有的线程在都达到某个状态后才会执行下一个步骤。在jdk中有一个更加好用的类,可以提供类似的作用,并且更简单,即CyclicBarrier类。CyclicBarrier类表示一个阻碍,在构造这个类时就要穿入一个值,表示标记的大小。在条件不满足时,所有调用await的线程都会被挂起,同时将一个标记减一,再将标记变为0之后,就会将所有阻塞的线程变为可以执行的。我们看一下他的源码:
1、构造方法:
public CyclicBarrier(int parties, Runnable barrierAction) {//parties表示要满足的调用await的个数,也就是上面说的要进行的聚会至少要来的人的个数。第二个参数是个可以执行的runnable,表示当条件满足之后(也就是partie变为0之后),要做的一件事,由最后触发await的线程执行。 if (parties <= 0) throw new IllegalArgumentException(); this.parties = parties;//这个是不变的,供 一个方法返回。 this.count = parties;//这个count是会不断减小的,每调用一次awati就会减一。 this.barrierCommand = barrierAction;//条件满足后要执行的任务 }
这个类中还有好多属性,
ReentrantLock lock :用于在调用await的时候加锁,防止并发修改count的值造成不准确。
Condition trip:从上面的lock中产生的condition,用于在不满足条件时将调用await的线程放到等待的队列中,当条件满足后将所有的等待的移动到lock(内部是aqs)的队列中。
Generation generation:这个属性狠简单,因为CyclicBarrier是一个可以重复使用的barrier,这个generation表示第几次使用,每当parties变为0之后这一代就算完了,再进行下一代的操作。它里面封装了一个boolean属性,表示当前的这一代有没有被破坏。当某个被阻塞的线程被调用interrupt之后就会变为true,即当前的一代被破坏了。
2、await方法
public int await() throws InterruptedException, BrokenBarrierException { try { return dowait(false, 0L);//从await中调用的是没有等待时间限制的dowait } catch (TimeoutException toe) { throw new Error(toe); // cannot happen; } }
第一个参数表示是否有时间限制,第二个参数是如果有时间限制的话,限制的时间
private int dowait(boolean timed, long nanos) throws InterruptedException, BrokenBarrierException, TimeoutException { final ReentrantLock lock = this.lock; lock.lock();//因为里面要修改count,所以加锁,防止并发修改 try { final Generation g = generation; if (g.broken) throw new BrokenBarrierException();//关于broken的我没有研究,所以不再对broken的情况进行注释。 if (Thread.interrupted()) { breakBarrier(); throw new InterruptedException(); } int index = --count;//减小count, if (index == 0) { // 刚上来不会是0的,这个是等到指定的次数被调用之后触发 boolean ranAction = false; try { final Runnable command = barrierCommand;//要执行的任务 if (command != null) command.run();//由最后调用await的线程调用,注意这里在执行run的时候可能会抛异常,就会进入finally的beakBarrier方法,其他线程在重新执行后就会全部抛出BrokenBarrierException异常。 ranAction = true; nextGeneration();//开启下一代,操作包括:1、将count重新设置为parties,2、更新generation的值,3、将全部condtion中阻塞的线程释放 return 0; } finally { if (!ranAction) breakBarrier(); } } // loop until tripped, broken, interrupted, or timed out for (;;) {//这个是当await没有调用指定的次数时 try { if (!timed)//如果没有时间限制,则调用condtion.await,也就是放入到conditon的队列中,将线程挂起 trip.await(); else if (nanos > 0L) nanos = trip.awaitNanos(nanos);//有时间限制的话,等待指定的时间, } catch (InterruptedException ie) { if (g == generation && ! g.broken) { breakBarrier();// throw ie; } else { // We're about to finish waiting even if we had not // been interrupted, so this interrupt is deemed to // "belong" to subsequent execution. Thread.currentThread().interrupt(); } } if (g.broken)//如果因为某个原因导致broken,则所有唤醒之后的线程都会抛出异常 throw new BrokenBarrierException(); if (g != generation) return index; if (timed && nanos <= 0L) {//如果是有时间限制的,如果等待超时候就会抛异常。 breakBarrier();//这个方法会将generation设置为broken,然后将condition中阻塞的线程全部释放。 throw new TimeoutException(); } } } finally { lock.unlock(); } }
在不考虑barrierBroken的前提下,这个类还是很容易看懂的。
相关推荐
本项目"tuling-juc-final.zip"显然聚焦于Java并发编程的实践,通过一系列代码示例来演示和解释Java内存模型(JMM)、`synchronized`关键字以及`volatile`关键字的使用。下面我们将深入探讨这些核心概念。 Java内存...
java - juc - 多线程 - 学习 -思维导图
Java-JUC-多线程进阶 Java-JUC-多线程进阶resources是 Java 并发编程的高级课程,涵盖了 Java 中的并发编程概念、线程安全、锁机制、集合类、线程池、函数式接口、Stream流式计算等多个方面。 什么是JUC JUC...
- **CountDownLatch/CyclicBarrier/Semaphore**:信号量类,用于控制并发线程数量或同步点。 8. **FutureTask** - **FutureTask**:表示一个异步计算的结果,它可以被取消,查询是否完成,获取或检查结果。 9. *...
8. **线程通信**:`Semaphore`(信号量)、`CountDownLatch`(倒计时器)、`CyclicBarrier`(循环栅栏)和`Phaser`(屏障)等工具类,用于控制线程间的同步和通信,实现特定的并发模式。 9. **Fork/Join框架**:...
"Java 多线程与并发(7-26)-JUC - 类汇总和学习指南" Java 多线程与并发是 Java 编程语言中的一部分,用于处理多线程和并发编程。Java 提供了一个名为 JUC(Java Utilities for Concurrency)的框架,用于帮助开发者...
JUC的同步器包括CountDownLatch、CyclicBarrier、Semaphore等,它们是为了解决多个线程协作完成特定任务的工具。例如,CountDownLatch可以让一些线程等待其他线程完成操作后再继续执行,而CyclicBarrier则让一组线程...
学习狂神说的juc编程的笔记
juc-jenkins-2018 JUC Jenkins 2018演示源代码 先决条件 为了运行此演示,必须有一个有效的JDK,git命令以及curl。 克隆存储库 将此存储库克隆到您家中的某个位置: git clone ...
【JUC 概念】 Java Util Concurrency (JUC) 是 Java SDK 中的一个核心包,位于 `java.util.concurrent` 下,它提供了丰富的线程同步和并发工具类,旨在简化多线程编程,提高程序的并发性能。JUC 包含了线程池、并发...
Java并发编程库(Java Util Concurrency,简称JUC)是Java平台中用于高效并发处理的重要工具,它提供了线程池、锁、原子变量等高级并发工具。在Java中,`java.util.concurrent`包包含了大量并发控制和并行计算的类与...
微程序控制器实验1. 连接好实验线路,检查无误后接通电源。2. 将编程开关(MJ20)置为PROM(编程)状态。3. 将STATE UNIT中的STEP置为“STEP”状态,STOP置为“RUN”状态。4. 在UA5-UA0开关上置要写的某个微地址(八进制)...
本项目"juc-learn"专注于JUC相关源码的分析和使用介绍,旨在帮助开发者深入理解并熟练运用这些并发工具。 1. **并发基础** 在Java中,多线程是并发编程的基础。通过创建Thread对象或实现Runnable接口,我们可以...
《JUC:Java并发编程的艺术》 在Java世界中,JUC(Java Util Concurrency)是并发编程的核心库,它提供了丰富的...通过深入学习JUC-master项目,我们可以更深入地理解Java并发编程的原理和实践,提升我们的编程技能。
juc-demo JUC包下常用工具练习Demo 内容: 1、Semaphore 2、CountDownLatch 3、CyclicBarrier 4、ReentrantLock + Condition实现阻塞队列 Created by @minghui.y.
Java 并发编程专题(九)----(JUC)浅析 CyclicBarrier CyclicBarrier 是 Java 并发编程中的一个同步辅助工具,它允许一组线程全部等待彼此到达公共屏障点。它的字面意思是可循环使用的屏障,用于让一组线程到达一...
1、Java并发体系-第一阶段-多线程基础知识 2、Java并发体系-第二阶段-锁与同步-[1] 3、Java并发体系-第二阶段-锁与同步-[2] 4、Java并发体系-第二阶段-锁与同步-[3] ...7、Java并发体系-第四阶段-AQS源码解读-[1]
JUC线程高级,
《JUC并发编程与源码分析视频课》是一门深入探讨Java并发编程的课程,主要聚焦于Java Util Concurrency(JUC)库的使用和源码解析。JUC是Java平台提供的一组高级并发工具包,它极大地简化了多线程编程,并提供了更...
教程视频:在 Java 5.0 提供了 java.util.concurrent(简称JUC)包,在此包中增加了在并发编程中很常用的工具类, 用于定义类似于线程的自定义子系统,包括线程池,异步 IO 和轻量级任务框架;还提供了设计用于多线程上下文...