countdownlatchcyclicbarriersemaphore的区别1和2主要做线程同步的1主要用于一个线程等待其他线程执行完成并且不能重用2只要用于多个线程相互等待做完之后一起乡下执行可以重置3主要控制资源访问量的三者都是基于aqs实现的要想实现多个线程之间相互同步可以用reentrlock的多条件等待实现Semaphore① 拿许可证acquire() —— 阻塞拿没许可就等着tryAcquire() —— 尝试拿拿不到拉倒acquireUninterruptibly() —— 死等拿天塌了也不停② 还许可证release() —— 还一个可跨线程还会累加③ 查状态availablePermits() —— 还剩几个drainPermits() —— 全部薅走getQueueLength() —— 多少人在等hasQueuedThreads() —— 有人在排队吗import java.util.concurrent.Semaphore;import java.util.concurrent.atomic.AtomicInteger;/** * 使用多个信号量控制多个线程顺序输出 1100。 * 信号量数组通过静态代码块初始化并预先释放第一个信号量。/public class SequentialPrinter { // 线程数量可调整 private static final int THREAD_COUNT 3; // 最大输出数字 private static final int MAX_NUMBER 100; // 每个线程对应的信号量静态成员 private static final Semaphore[] semaphores new Semaphore[THREAD_COUNT]; // 当前待打印的数字原子操作保证线程安全 private static final AtomicInteger currentNumber new AtomicInteger(1); // 静态代码块初始化所有信号量并让第一个信号量获得一个许可 static { for (int i 0; i THREAD_COUNT; i) { semaphores[i] new Semaphore(0); } semaphores[0].release(); // 第 0 号线程可以先执行 } /* * 打印任务每个线程持有一个自己的信号量和一个下一个线程的信号量 */ static class Printer implements Runnable { private final int threadId; private final Semaphore mySemaphore; private final Semaphore nextSemaphore; public Printer(int threadId, Semaphore mySemaphore, Semaphore nextSemaphore) { this.threadId threadId; this.mySemaphore mySemaphore; this.nextSemaphore nextSemaphore; } Override public void run() { while (true) { try { // 获取自己的信号量没有许可则阻塞 mySemaphore.acquire(); int num currentNumber.getAndIncrement(); if (num MAX_NUMBER) { System.out.println(“Thread-” threadId 输出: num); // 唤醒下一个线程 nextSemaphore.release(); } else { // 数字已超限传递退出信号给下一个线程 nextSemaphore.release(); break; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } } public static void main(String[] args) throws InterruptedException { // 创建并启动线程 Thread[] threads new Thread[THREAD_COUNT]; for (int i 0; i THREAD_COUNT; i) { Semaphore my semaphores[i]; Semaphore next semaphores[(i 1) % THREAD_COUNT]; Printer printer new Printer(i, my, next); threads[i] new Thread(printer, “Printer-” i); threads[i].start(); } // 等待所有线程结束 for (Thread t : threads) { t.join(); } System.out.println(“所有线程已退出1~” MAX_NUMBER 顺序输出完毕。); }}CountDownLatch 是 Java 并发包java.util.concurrent中的一个同步工具类。它允许一个或多个线程等待直到在其他线程中执行的一组操作完成。可以理解为一个倒计数器初始计数设置为 N每调用一次 countDown() 计数减 1当计数减到 0 时所有在 await() 上等待的线程被唤醒继续执行。方法 说明CountDownLatch(int count) 构造方法指定初始计数必须 ≥ 0。如果 count 0则 await() 会立即通过。void await() 使当前线程等待直到计数变为 0。如果计数已为 0则直接返回。可响应中断抛出 InterruptedException。boolean await(long timeout, TimeUnit unit) 带超时的等待。如果超时时间内计数未变为 0则返回 false否则返回 true。void countDown() 将计数减 1。如果减后计数变为 0则唤醒所有在 await() 上等待的线程。long getCount() 返回当前计数值调试或监控用不是同步安全的通常业务逻辑。注意计数不能重置。当计数变为 0 后后续 await() 立即通过countDown() 不再生效。示例一主线程等待多个异步任务完成后再处理结果一等多publicclassBatchProcess{publicstaticvoidmain(String[]args)throwsInterruptedException{inttaskCount5;CountDownLatchlatchnewCountDownLatch(taskCount);ExecutorServiceexecutorExecutors.newFixedThreadPool(taskCount);for(inti0;itaskCount;i){finalinttaskIdi;executor.submit(()-{try{// 模拟异步任务Thread.sleep(1000);System.out.println(任务 taskId 完成);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}finally{latch.countDown();// 无论成功/失败都要减计数}});}// 主线程等待所有任务完成latch.await();System.out.println(所有任务已完成开始汇总结果...);executor.shutdown();}}示例 2多个线程同时开始执行多等一模拟并发publicclassConcurrentStarter{publicstaticvoidmain(String[]args)throwsInterruptedException{intworkerCount10;CountDownLatchstartSignalnewCountDownLatch(1);// 控制起跑CountDownLatchdoneSignalnewCountDownLatch(workerCount);// 等待完成for(inti0;iworkerCount;i){newThread(()-{try{startSignal.await();// 所有线程在此等待// 执行并发任务System.out.println(Thread.currentThread().getName() 开始工作);Thread.sleep(500);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}finally{doneSignal.countDown();}}).start();}System.out.println(准备就绪3秒后所有线程同时启动...);Thread.sleep(3000);startSignal.countDown();// 发令枪响doneSignal.await();// 主线程等待所有工作线程结束System.out.println(全部工作完成);}}示例 3分布式系统 – 等待多个服务健康检查完成publicclassServiceHealthChecker{privatestaticfinalListStringSERVICESArrays.asList(DB,Redis,MQ,ConfigCenter);publicstaticvoidmain(String[]args)throwsInterruptedException{CountDownLatchlatchnewCountDownLatch(SERVICES.size());for(Stringservice:SERVICES){newThread(()-{try{// 模拟检查服务是否可用booleanhealthycheckService(service);if(healthy){System.out.println(service is healthy.);}else{System.err.println(service is NOT healthy.);}}finally{latch.countDown();}}).start();}// 最多等待10秒所有服务必须就绪if(latch.await(10,TimeUnit.SECONDS)){System.out.println(所有服务检查完成应用启动...);}else{System.err.println(某些服务未在10秒内响应启动失败);System.exit(1);}}privatestaticbooleancheckService(Stringservice)throwsInterruptedException{Thread.sleep(500);// 模拟网络请求returntrue;// 简化示例}}CyclicBarrier是Java并发包java.util.concurrent中的一个同步辅助类它允许一组线程互相等待直到所有线程都到达某个公共屏障点barrier后才能继续执行后续任务。 与CountDownLatch不同CyclicBarrier的计数器可以循环使用Cyclic意为“循环的”。 方法 说明CyclicBarrier(intparties)构造方法设置需要等待的线程数量parties。CyclicBarrier(intparties,RunnablebarrierAction)构造方法当所有线程都到达屏障时优先执行 barrierAction由最后一个到达的线程执行。intawait()使当前线程等待直到所有 parties 都调用了await()。如果当前线程是最后一个到达的则会唤醒所有等待线程并可选执行 barrierAction。返回当前线程到达的索引0表示最后一个。可响应中断。intawait(longtimeout,TimeUnitunit)带超时的等待超时后抛出TimeoutException其他等待线程会收到BrokenBarrierException。voidreset()将屏障重置到初始状态如果此时有线程在等待会抛出BrokenBarrierException。intgetNumberWaiting()返回当前在屏障处等待的线程数量。booleanisBroken()判断屏障是否被破坏例如等待时超时或中断。 示例1基本用法——多线程互相等待一起继续 javaimportjava.util.concurrent.BrokenBarrierException;importjava.util.concurrent.CyclicBarrier;publicclassCyclicBarrierDemo{publicstaticvoidmain(String[]args){intthreadCount3;CyclicBarrierbarriernewCyclicBarrier(threadCount,()-{// 所有线程到达屏障后由最后一个到达的线程执行此任务System.out.println(所有线程已到达屏障继续执行下一阶段...);});for(inti0;ithreadCount;i){finalintidi;newThread(()-{try{System.out.println(线程 id 开始执行第一阶段任务...);Thread.sleep((long)(Math.random()*1000));System.out.println(线程 id 到达屏障);barrier.await();// 等待其他线程System.out.println(线程 id 进入第二阶段);Thread.sleep((long)(Math.random()*1000));System.out.println(线程 id 完成第二阶段);// 屏障可重复使用如果需要再调用 barrier.await() 即可}catch(InterruptedException|BrokenBarrierExceptione){e.printStackTrace();}}).start();}}}输出示例顺序可能不同线程 0 开始执行第一阶段任务... 线程 1 开始执行第一阶段任务... 线程 2 开始执行第一阶段任务... 线程 1 到达屏障 线程 0 到达屏障 线程 2 到达屏障 所有线程已到达屏障继续执行下一阶段... 线程 2 进入第二阶段 线程 1 进入第二阶段 线程 0 进入第二阶段 线程 0 完成第二阶段 线程 1 完成第二阶段 线程 2 完成第二阶段示例 2可重用的 CyclicBarrier模拟多轮并发起跑publicclassReusableBarrier{publicstaticvoidmain(String[]args){intrunners5;CyclicBarrierstartLinenewCyclicBarrier(runners,()-System.out.println(--- 发令枪响所有运动员起跑 ---));for(inti0;irunners;i){finalintathletei;newThread(()-{try{// 第一轮System.out.println(运动员 athlete 到达起跑线等待...);startLine.await();System.out.println(运动员 athlete 开始跑);Thread.sleep(500);System.out.println(运动员 athlete 第一轮结束);// 第二轮重用同一个 BarrierSystem.out.println(运动员 athlete 回到起跑线等待第二轮...);startLine.await();System.out.println(运动员 athlete 第二轮开始跑);Thread.sleep(500);System.out.println(运动员 athlete 第二轮结束);}catch(Exceptione){e.printStackTrace();}}).start();}}}示例 3带超时的等待与重置CyclicBarrierbarriernewCyclicBarrier(3);try{barrier.await(1,TimeUnit.SECONDS);}catch(TimeoutExceptione){System.out.println(超时屏障被破坏);barrier.reset();// 重置屏障其他等待线程会抛出 BrokenBarrierException}特性 CyclicBarrier CountDownLatch可重用性 可重复使用调用 reset() 或自动重置 不可重用计数器减到 0 后无法恢复等待线程数 固定数量的线程互相等待必须相等 主线程或任意线程等待其他 N 个线程完成构造参数 parties参与的线程数 count需要 countDown 的次数计数方式 调用 await() 计数减 1到达屏障 调用 countDown() 计数减 1屏障动作 支持 barrierAction最后一个到达的线程执行 无异常处理 一个线程中断/超时会破坏屏障其他线程收到 BrokenBarrierException 单个线程中断只影响该线程不影响其他线程典型场景 多线程分阶段计算、并行迭代、可重用的并发起跑 主线程等待多个子任务完成、多等一、一等多ReentrantLock 常用方法三段式记忆法① 拿锁lock() —— 阻塞拿拿不到就等tryLock() —— 尝试拿拿不到拉倒tryLock(时间) —— 限时等超时放弃lockInterruptibly() —— 可中断拿别人 interrupt 我就停② 还锁unlock() —— 必须释放成对出现finally 里最稳③ 高级功能newCondition() —— 创建条件变量精确唤醒condition对象只有唤醒阻塞两种方法getHoldCount() —— 当前线程重入次数isHeldByCurrentThread() —— 锁是不是我拿着getQueueLength() —— 等锁的有多少人isFair() —— 是否公平锁用法import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;/**使用 ReentrantLock Condition 控制多个线程顺序输出 1100。每个线程有一个对应的 Condition通过锁和条件变量实现精确唤醒。*/public class SequentialPrinterWithLock {// 线程数量可调整private static final int THREAD_COUNT 3;// 最大输出数字private static final int MAX_NUMBER 100;// 共享锁和条件数组private static final ReentrantLock lock new ReentrantLock();private static final Condition[] conditions new Condition[THREAD_COUNT];// 共享状态private static int currentNumber 1; // 下一个待输出的数字private static int currentThreadId 0; // 当前应该运行的线程编号 (0 ~ THREAD_COUNT-1)// 静态代码块初始化所有 Conditionstatic {for (int i 0; i THREAD_COUNT; i) {conditions[i] lock.newCondition();}}/**每个线程的任务输出数字并唤醒下一个线程*/static class Printer implements Runnable {private final int threadId;public Printer(int threadId) {this.threadId threadId;}Overridepublic void run() {while (true) {lock.lock();try {// 如果不是当前线程应该运行的编号则等待while (currentThreadId ! threadId) {conditions[threadId].await();}// 检查是否已经输出完所有数字 if (currentNumber MAX_NUMBER) { // 通知下一个线程退出避免死等 int nextId (currentThreadId 1) % THREAD_COUNT; conditions[nextId].signal(); break; } // 输出当前数字 System.out.println(Thread- threadId 输出: currentNumber); currentNumber; // 切换到下一个线程 currentThreadId (currentThreadId 1) % THREAD_COUNT; // 唤醒下一个线程 conditions[currentThreadId].signal(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } finally { lock.unlock(); } }}}public static void main(String[] args) throws InterruptedException {// 创建并启动所有线程Thread[] threads new Thread[THREAD_COUNT];for (int i 0; i THREAD_COUNT; i) {threads[i] new Thread(new Printer(i), “Printer-” i);threads[i].start();}// 等待所有线程结束 for (Thread t : threads) { t.join(); } System.out.println(所有线程已退出1~ MAX_NUMBER 顺序输出完毕。);}}reentrantlock和synchronized的区别synchronized是jvm层面的锁通过监视器monitor对象实现在要加锁的字节码前后加上monitorentermonitorexit字节码指令来实现自动加锁解锁默认非公平ReentrantLock是API级别实现需要显式lock()和unlock()但支持公平锁、可中断锁获取、尝试获取锁和多个条件变量。性能上Java是1.6后两者相当sync通过锁升级优化了性能选择时建议简单场景用synchronized保持代码简洁当需要可中断性、超时控制、公平性或多条件等待等高级功能时选用ReentrantLock。使用时必须将unlock()放在finally块中确保锁释放避免死锁。在synchronized锁字符串时1.字符串可能会被jvm放入常量池就是内容相同的字符串字面量在内存中只有一个如果多个锁锁同一对象可能相互干扰2.new string“lock”每次产生新的对象锁的就不是一个对象无法互斥3.锁对象引用被重新赋值后后续对象使用新锁锁被破坏4.行为不确定性 拼接、intern() 等操作使锁对象的身份难以判断5.synchronized 的锁对象。最安全、清晰的做法是使用专门的 private final Object 实例作为锁。这样不仅能避免上述陷阱
Java并发实用干货
countdownlatchcyclicbarriersemaphore的区别1和2主要做线程同步的1主要用于一个线程等待其他线程执行完成并且不能重用2只要用于多个线程相互等待做完之后一起乡下执行可以重置3主要控制资源访问量的三者都是基于aqs实现的要想实现多个线程之间相互同步可以用reentrlock的多条件等待实现Semaphore① 拿许可证acquire() —— 阻塞拿没许可就等着tryAcquire() —— 尝试拿拿不到拉倒acquireUninterruptibly() —— 死等拿天塌了也不停② 还许可证release() —— 还一个可跨线程还会累加③ 查状态availablePermits() —— 还剩几个drainPermits() —— 全部薅走getQueueLength() —— 多少人在等hasQueuedThreads() —— 有人在排队吗import java.util.concurrent.Semaphore;import java.util.concurrent.atomic.AtomicInteger;/** * 使用多个信号量控制多个线程顺序输出 1100。 * 信号量数组通过静态代码块初始化并预先释放第一个信号量。/public class SequentialPrinter { // 线程数量可调整 private static final int THREAD_COUNT 3; // 最大输出数字 private static final int MAX_NUMBER 100; // 每个线程对应的信号量静态成员 private static final Semaphore[] semaphores new Semaphore[THREAD_COUNT]; // 当前待打印的数字原子操作保证线程安全 private static final AtomicInteger currentNumber new AtomicInteger(1); // 静态代码块初始化所有信号量并让第一个信号量获得一个许可 static { for (int i 0; i THREAD_COUNT; i) { semaphores[i] new Semaphore(0); } semaphores[0].release(); // 第 0 号线程可以先执行 } /* * 打印任务每个线程持有一个自己的信号量和一个下一个线程的信号量 */ static class Printer implements Runnable { private final int threadId; private final Semaphore mySemaphore; private final Semaphore nextSemaphore; public Printer(int threadId, Semaphore mySemaphore, Semaphore nextSemaphore) { this.threadId threadId; this.mySemaphore mySemaphore; this.nextSemaphore nextSemaphore; } Override public void run() { while (true) { try { // 获取自己的信号量没有许可则阻塞 mySemaphore.acquire(); int num currentNumber.getAndIncrement(); if (num MAX_NUMBER) { System.out.println(“Thread-” threadId 输出: num); // 唤醒下一个线程 nextSemaphore.release(); } else { // 数字已超限传递退出信号给下一个线程 nextSemaphore.release(); break; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } } public static void main(String[] args) throws InterruptedException { // 创建并启动线程 Thread[] threads new Thread[THREAD_COUNT]; for (int i 0; i THREAD_COUNT; i) { Semaphore my semaphores[i]; Semaphore next semaphores[(i 1) % THREAD_COUNT]; Printer printer new Printer(i, my, next); threads[i] new Thread(printer, “Printer-” i); threads[i].start(); } // 等待所有线程结束 for (Thread t : threads) { t.join(); } System.out.println(“所有线程已退出1~” MAX_NUMBER 顺序输出完毕。); }}CountDownLatch 是 Java 并发包java.util.concurrent中的一个同步工具类。它允许一个或多个线程等待直到在其他线程中执行的一组操作完成。可以理解为一个倒计数器初始计数设置为 N每调用一次 countDown() 计数减 1当计数减到 0 时所有在 await() 上等待的线程被唤醒继续执行。方法 说明CountDownLatch(int count) 构造方法指定初始计数必须 ≥ 0。如果 count 0则 await() 会立即通过。void await() 使当前线程等待直到计数变为 0。如果计数已为 0则直接返回。可响应中断抛出 InterruptedException。boolean await(long timeout, TimeUnit unit) 带超时的等待。如果超时时间内计数未变为 0则返回 false否则返回 true。void countDown() 将计数减 1。如果减后计数变为 0则唤醒所有在 await() 上等待的线程。long getCount() 返回当前计数值调试或监控用不是同步安全的通常业务逻辑。注意计数不能重置。当计数变为 0 后后续 await() 立即通过countDown() 不再生效。示例一主线程等待多个异步任务完成后再处理结果一等多publicclassBatchProcess{publicstaticvoidmain(String[]args)throwsInterruptedException{inttaskCount5;CountDownLatchlatchnewCountDownLatch(taskCount);ExecutorServiceexecutorExecutors.newFixedThreadPool(taskCount);for(inti0;itaskCount;i){finalinttaskIdi;executor.submit(()-{try{// 模拟异步任务Thread.sleep(1000);System.out.println(任务 taskId 完成);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}finally{latch.countDown();// 无论成功/失败都要减计数}});}// 主线程等待所有任务完成latch.await();System.out.println(所有任务已完成开始汇总结果...);executor.shutdown();}}示例 2多个线程同时开始执行多等一模拟并发publicclassConcurrentStarter{publicstaticvoidmain(String[]args)throwsInterruptedException{intworkerCount10;CountDownLatchstartSignalnewCountDownLatch(1);// 控制起跑CountDownLatchdoneSignalnewCountDownLatch(workerCount);// 等待完成for(inti0;iworkerCount;i){newThread(()-{try{startSignal.await();// 所有线程在此等待// 执行并发任务System.out.println(Thread.currentThread().getName() 开始工作);Thread.sleep(500);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}finally{doneSignal.countDown();}}).start();}System.out.println(准备就绪3秒后所有线程同时启动...);Thread.sleep(3000);startSignal.countDown();// 发令枪响doneSignal.await();// 主线程等待所有工作线程结束System.out.println(全部工作完成);}}示例 3分布式系统 – 等待多个服务健康检查完成publicclassServiceHealthChecker{privatestaticfinalListStringSERVICESArrays.asList(DB,Redis,MQ,ConfigCenter);publicstaticvoidmain(String[]args)throwsInterruptedException{CountDownLatchlatchnewCountDownLatch(SERVICES.size());for(Stringservice:SERVICES){newThread(()-{try{// 模拟检查服务是否可用booleanhealthycheckService(service);if(healthy){System.out.println(service is healthy.);}else{System.err.println(service is NOT healthy.);}}finally{latch.countDown();}}).start();}// 最多等待10秒所有服务必须就绪if(latch.await(10,TimeUnit.SECONDS)){System.out.println(所有服务检查完成应用启动...);}else{System.err.println(某些服务未在10秒内响应启动失败);System.exit(1);}}privatestaticbooleancheckService(Stringservice)throwsInterruptedException{Thread.sleep(500);// 模拟网络请求returntrue;// 简化示例}}CyclicBarrier是Java并发包java.util.concurrent中的一个同步辅助类它允许一组线程互相等待直到所有线程都到达某个公共屏障点barrier后才能继续执行后续任务。 与CountDownLatch不同CyclicBarrier的计数器可以循环使用Cyclic意为“循环的”。 方法 说明CyclicBarrier(intparties)构造方法设置需要等待的线程数量parties。CyclicBarrier(intparties,RunnablebarrierAction)构造方法当所有线程都到达屏障时优先执行 barrierAction由最后一个到达的线程执行。intawait()使当前线程等待直到所有 parties 都调用了await()。如果当前线程是最后一个到达的则会唤醒所有等待线程并可选执行 barrierAction。返回当前线程到达的索引0表示最后一个。可响应中断。intawait(longtimeout,TimeUnitunit)带超时的等待超时后抛出TimeoutException其他等待线程会收到BrokenBarrierException。voidreset()将屏障重置到初始状态如果此时有线程在等待会抛出BrokenBarrierException。intgetNumberWaiting()返回当前在屏障处等待的线程数量。booleanisBroken()判断屏障是否被破坏例如等待时超时或中断。 示例1基本用法——多线程互相等待一起继续 javaimportjava.util.concurrent.BrokenBarrierException;importjava.util.concurrent.CyclicBarrier;publicclassCyclicBarrierDemo{publicstaticvoidmain(String[]args){intthreadCount3;CyclicBarrierbarriernewCyclicBarrier(threadCount,()-{// 所有线程到达屏障后由最后一个到达的线程执行此任务System.out.println(所有线程已到达屏障继续执行下一阶段...);});for(inti0;ithreadCount;i){finalintidi;newThread(()-{try{System.out.println(线程 id 开始执行第一阶段任务...);Thread.sleep((long)(Math.random()*1000));System.out.println(线程 id 到达屏障);barrier.await();// 等待其他线程System.out.println(线程 id 进入第二阶段);Thread.sleep((long)(Math.random()*1000));System.out.println(线程 id 完成第二阶段);// 屏障可重复使用如果需要再调用 barrier.await() 即可}catch(InterruptedException|BrokenBarrierExceptione){e.printStackTrace();}}).start();}}}输出示例顺序可能不同线程 0 开始执行第一阶段任务... 线程 1 开始执行第一阶段任务... 线程 2 开始执行第一阶段任务... 线程 1 到达屏障 线程 0 到达屏障 线程 2 到达屏障 所有线程已到达屏障继续执行下一阶段... 线程 2 进入第二阶段 线程 1 进入第二阶段 线程 0 进入第二阶段 线程 0 完成第二阶段 线程 1 完成第二阶段 线程 2 完成第二阶段示例 2可重用的 CyclicBarrier模拟多轮并发起跑publicclassReusableBarrier{publicstaticvoidmain(String[]args){intrunners5;CyclicBarrierstartLinenewCyclicBarrier(runners,()-System.out.println(--- 发令枪响所有运动员起跑 ---));for(inti0;irunners;i){finalintathletei;newThread(()-{try{// 第一轮System.out.println(运动员 athlete 到达起跑线等待...);startLine.await();System.out.println(运动员 athlete 开始跑);Thread.sleep(500);System.out.println(运动员 athlete 第一轮结束);// 第二轮重用同一个 BarrierSystem.out.println(运动员 athlete 回到起跑线等待第二轮...);startLine.await();System.out.println(运动员 athlete 第二轮开始跑);Thread.sleep(500);System.out.println(运动员 athlete 第二轮结束);}catch(Exceptione){e.printStackTrace();}}).start();}}}示例 3带超时的等待与重置CyclicBarrierbarriernewCyclicBarrier(3);try{barrier.await(1,TimeUnit.SECONDS);}catch(TimeoutExceptione){System.out.println(超时屏障被破坏);barrier.reset();// 重置屏障其他等待线程会抛出 BrokenBarrierException}特性 CyclicBarrier CountDownLatch可重用性 可重复使用调用 reset() 或自动重置 不可重用计数器减到 0 后无法恢复等待线程数 固定数量的线程互相等待必须相等 主线程或任意线程等待其他 N 个线程完成构造参数 parties参与的线程数 count需要 countDown 的次数计数方式 调用 await() 计数减 1到达屏障 调用 countDown() 计数减 1屏障动作 支持 barrierAction最后一个到达的线程执行 无异常处理 一个线程中断/超时会破坏屏障其他线程收到 BrokenBarrierException 单个线程中断只影响该线程不影响其他线程典型场景 多线程分阶段计算、并行迭代、可重用的并发起跑 主线程等待多个子任务完成、多等一、一等多ReentrantLock 常用方法三段式记忆法① 拿锁lock() —— 阻塞拿拿不到就等tryLock() —— 尝试拿拿不到拉倒tryLock(时间) —— 限时等超时放弃lockInterruptibly() —— 可中断拿别人 interrupt 我就停② 还锁unlock() —— 必须释放成对出现finally 里最稳③ 高级功能newCondition() —— 创建条件变量精确唤醒condition对象只有唤醒阻塞两种方法getHoldCount() —— 当前线程重入次数isHeldByCurrentThread() —— 锁是不是我拿着getQueueLength() —— 等锁的有多少人isFair() —— 是否公平锁用法import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;/**使用 ReentrantLock Condition 控制多个线程顺序输出 1100。每个线程有一个对应的 Condition通过锁和条件变量实现精确唤醒。*/public class SequentialPrinterWithLock {// 线程数量可调整private static final int THREAD_COUNT 3;// 最大输出数字private static final int MAX_NUMBER 100;// 共享锁和条件数组private static final ReentrantLock lock new ReentrantLock();private static final Condition[] conditions new Condition[THREAD_COUNT];// 共享状态private static int currentNumber 1; // 下一个待输出的数字private static int currentThreadId 0; // 当前应该运行的线程编号 (0 ~ THREAD_COUNT-1)// 静态代码块初始化所有 Conditionstatic {for (int i 0; i THREAD_COUNT; i) {conditions[i] lock.newCondition();}}/**每个线程的任务输出数字并唤醒下一个线程*/static class Printer implements Runnable {private final int threadId;public Printer(int threadId) {this.threadId threadId;}Overridepublic void run() {while (true) {lock.lock();try {// 如果不是当前线程应该运行的编号则等待while (currentThreadId ! threadId) {conditions[threadId].await();}// 检查是否已经输出完所有数字 if (currentNumber MAX_NUMBER) { // 通知下一个线程退出避免死等 int nextId (currentThreadId 1) % THREAD_COUNT; conditions[nextId].signal(); break; } // 输出当前数字 System.out.println(Thread- threadId 输出: currentNumber); currentNumber; // 切换到下一个线程 currentThreadId (currentThreadId 1) % THREAD_COUNT; // 唤醒下一个线程 conditions[currentThreadId].signal(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } finally { lock.unlock(); } }}}public static void main(String[] args) throws InterruptedException {// 创建并启动所有线程Thread[] threads new Thread[THREAD_COUNT];for (int i 0; i THREAD_COUNT; i) {threads[i] new Thread(new Printer(i), “Printer-” i);threads[i].start();}// 等待所有线程结束 for (Thread t : threads) { t.join(); } System.out.println(所有线程已退出1~ MAX_NUMBER 顺序输出完毕。);}}reentrantlock和synchronized的区别synchronized是jvm层面的锁通过监视器monitor对象实现在要加锁的字节码前后加上monitorentermonitorexit字节码指令来实现自动加锁解锁默认非公平ReentrantLock是API级别实现需要显式lock()和unlock()但支持公平锁、可中断锁获取、尝试获取锁和多个条件变量。性能上Java是1.6后两者相当sync通过锁升级优化了性能选择时建议简单场景用synchronized保持代码简洁当需要可中断性、超时控制、公平性或多条件等待等高级功能时选用ReentrantLock。使用时必须将unlock()放在finally块中确保锁释放避免死锁。在synchronized锁字符串时1.字符串可能会被jvm放入常量池就是内容相同的字符串字面量在内存中只有一个如果多个锁锁同一对象可能相互干扰2.new string“lock”每次产生新的对象锁的就不是一个对象无法互斥3.锁对象引用被重新赋值后后续对象使用新锁锁被破坏4.行为不确定性 拼接、intern() 等操作使锁对象的身份难以判断5.synchronized 的锁对象。最安全、清晰的做法是使用专门的 private final Object 实例作为锁。这样不仅能避免上述陷阱