package com.example.demo; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Random; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 关于计数器(countdownlathch)的使用 * 当计数器为0时,唤醒使用计数器锁住的线程,注意一个线程可能会执行俩次,可能需要sychronized去控制 */ public class CountdownTest { public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch(3); // 构造一个线程池 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3), new ThreadPoolExecutor.DiscardOldestPolicy()); Thread t1 = new Thread(new Customer(countDownLatch, "t1")); Thread t2 = new Thread(new Customer(countDownLatch, "t2")); Thread t3 = new Thread(new Customer(countDownLatch, "t3")); Thread t4 = new Thread(new Waitress(countDownLatch, "t4")); threadPool.execute(t1); threadPool.execute(t2); threadPool.execute(t3); threadPool.execute(t4); } } class Customer implements Runnable { private CountDownLatch latch; private String name; public Customer(CountDownLatch latch, String name) { this.latch = latch; this.name = name; } @Override public void run() { try { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); Random random = new Random(); System.out.println(sdf.format(new Date()) + " " + name + "出发去饭店"); Thread.sleep((long) (random.nextDouble() * 3000) + 1000); System.out.println(sdf.format(new Date()) + " " + name + "到了饭店"); latch.countDown(); } catch (Exception e) { e.printStackTrace(); } } } class Waitress implements Runnable { private CountDownLatch latch; private String name; public Waitress(CountDownLatch latch, String name) { this.latch = latch; this.name = name; } @Override public void run() { try { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); System.out.println(sdf.format(new Date()) + " " + name + "等待顾客"); latch.await(); System.out.println(sdf.format(new Date()) + " " + name + "开始上菜"); } catch (Exception e) { e.printStackTrace(); } } }
latch.countDown(); //===================== public void countDown() { sync.releaseShared(1); } //===================== public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; } //===================== private void doReleaseShared() { /* * Ensure that a release propagates, even if there are other * in-progress acquires/releases. This proceeds in the usual * way of trying to unparkSuccessor of head if it needs * signal. But if it does not, status is set to PROPAGATE to * ensure that upon release, propagation continues. * Additionally, we must loop in case a new node is added * while we are doing this. Also, unlike other uses of * unparkSuccessor, we need to know if CAS to reset status * fails, if so rechecking. */ for (;;) { Node h = head; if (h != null && h != tail) { int ws = h.waitStatus; if (ws == Node.SIGNAL) { if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; // loop to recheck cases unparkSuccessor(h); } else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } if (h == head) // loop if head changed break; } }
latch.await(); //======================= public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } //======================= public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); } //======================= private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }