Concurrent---工具类(CountDownLatch、CycliBarrier、Semaphore)

本文深入探讨了Java并发控制的三大核心工具:CountDownLatch、CyclicBarrier和Semaphore。CountDownLatch用于线程间的等待通知机制,CyclicBarrier实现线程的周期性屏障同步,而Semaphore则控制线程访问资源的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CountDownLatch

介绍

java.util.concurrnet.CountDownLatch

是一个同步辅助类,利用它可以实现类似计数器的功能,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

用一个给定的计数来初始化CountDownLatch,通过调用countDown()方法使计数减1,在计数达到0之前,被await()阻塞的线程会一直阻塞,之后,会释放所有等待的线程。

使用场景

在一些应用场合中,需要等待某个条件达到要求后才能做后面的事情, 同时当线程都完成后也会出发事件,以便进行后面的操作。这个时候就可以使用CountDownLatch

CountDownLatch最重要的方法时countDown()和await(),前者主要是将计数减1,后者是等待计数为0,如果没达到0,就只能阻塞等待。

小案例


import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
	
	public static void main(String[] args) throws InterruptedException {
		
		CountDownLatch cdl = new CountDownLatch(5);
		
		new Thread(new Teacher(cdl)).start();
		new Thread(new Student(cdl)).start();
		new Thread(new Student(cdl)).start();
		new Thread(new Student(cdl)).start();
		new Thread(new Student(cdl)).start();
		
		// 表示让线程阻塞,直到计数归零的时候阻塞才能放开
		cdl.await();
		
		System.out.println("考试结束~~~");
		
	}

}

class Student implements Runnable {
	
	private CountDownLatch cdl;

	public Student(CountDownLatch cdl) {
		this.cdl = cdl;
	}

	@Override
	public void run() {
		
		System.out.println("学生来到考场,准备考试~~~");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		System.out.println("学生交卷离开考场");
		
		// 计数-1
		cdl.countDown();
	}
}

class Teacher implements Runnable {
	
	private CountDownLatch cdl;

	public Teacher(CountDownLatch cdl) {
		this.cdl = cdl;
	}

	@Override
	public void run() {

		System.out.println("老师来到考场,准备考试~~~");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("老师收卷离开了考场~~~");
		
		cdl.countDown();
	}

}

CyclicBarrier用法

字面意思回环栅栏–当所有的线程都到达了同一个点之后才能继续往下执行.
当所有等待线程都被释放以后,CyclicBarrier可以被重用。

CyclicBarrier类位于java.util.concurrent包下

构造器

//parties:指让多少个线程或者任务等待,
//barrierAction为当这些线程都达到barrier状态时会执行的内容。
public CyclicBarrier(int parties, Runnable barrierAction) {
}
 
public CyclicBarrier(int parties) {
}

重要方法

public int await() throws InterruptedException, BrokenBarrierException { };
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };

第一种比较常用:用来挂起线程,直到所有的线程都达到同一个状态再同时执行后续任务。

第二个方法:让这些线程等待至一定时间,如果还有线程没达到就直接让达到的线程执行后续任务。

package cn.tedu.concurrent2;


import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo1 {

    public static void main(String[] args) {

        CountDownLatch latch = new CountDownLatch(1);

        CyclicBarrier barrier = new CyclicBarrier(10);


        Thread caipan = new Thread(new CaiPan(latch));

        caipan.start();


        for (int i = 0 ; i<10 ;i++){

           Thread sport =  new Thread(new SportMan(barrier));

            sport.start();
        }

    }





}

class SportMan implements Runnable{
    CyclicBarrier barrier = null;

    public SportMan(CyclicBarrier barrier) {
        this.barrier = barrier;
    }

    @Override
    public void run() {

        System.out.println(Thread.currentThread().getName()+"选手到达起跑线");
        try {
            barrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+"冲出了起跑线");

    }
}
class CaiPan implements  Runnable{
    CountDownLatch latch = null;

    public CaiPan(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        System.out.println("比赛开始");
        latch.countDown();
    }
}

执行结果

D:\java\jdk1.8.0\bin\java.exe "-javaagent:D:\IDEA\IntelliJ IDEA 2018.2.5\lib\idea_rt.jar=54203:D:\IDEA\IntelliJ IDEA 2018.2.5\bin" -Dfile.encoding=UTF-8 -classpath D:\java\jdk1.8.0\jre\lib\charsets.jar;D:\java\jdk1.8.0\jre\lib\deploy.jar;D:\java\jdk1.8.0\jre\lib\ext\access-bridge-64.jar;D:\java\jdk1.8.0\jre\lib\ext\cldrdata.jar;D:\java\jdk1.8.0\jre\lib\ext\dnsns.jar;D:\java\jdk1.8.0\jre\lib\ext\jaccess.jar;D:\java\jdk1.8.0\jre\lib\ext\jfxrt.jar;D:\java\jdk1.8.0\jre\lib\ext\localedata.jar;D:\java\jdk1.8.0\jre\lib\ext\nashorn.jar;D:\java\jdk1.8.0\jre\lib\ext\sunec.jar;D:\java\jdk1.8.0\jre\lib\ext\sunjce_provider.jar;D:\java\jdk1.8.0\jre\lib\ext\sunmscapi.jar;D:\java\jdk1.8.0\jre\lib\ext\sunpkcs11.jar;D:\java\jdk1.8.0\jre\lib\ext\zipfs.jar;D:\java\jdk1.8.0\jre\lib\javaws.jar;D:\java\jdk1.8.0\jre\lib\jce.jar;D:\java\jdk1.8.0\jre\lib\jfr.jar;D:\java\jdk1.8.0\jre\lib\jfxswt.jar;D:\java\jdk1.8.0\jre\lib\jsse.jar;D:\java\jdk1.8.0\jre\lib\management-agent.jar;D:\java\jdk1.8.0\jre\lib\plugin.jar;D:\java\jdk1.8.0\jre\lib\resources.jar;D:\java\jdk1.8.0\jre\lib\rt.jar;E:\Test\out\production\ClassLoadDemo cn.tedu.concurrent2.CyclicBarrierDemo1
比赛开始
Thread-4选手到达起跑线
Thread-9选手到达起跑线
Thread-10选手到达起跑线
Thread-3选手到达起跑线
Thread-1选手到达起跑线
Thread-5选手到达起跑线
Thread-2选手到达起跑线
Thread-6选手到达起跑线
Thread-8选手到达起跑线
Thread-7选手到达起跑线
Thread-7冲出了起跑线
Thread-4冲出了起跑线
Thread-9冲出了起跑线
Thread-10冲出了起跑线
Thread-3冲出了起跑线
Thread-1冲出了起跑线
Thread-5冲出了起跑线
Thread-6冲出了起跑线
Thread-8冲出了起跑线
Thread-2冲出了起跑线

Process finished with exit code 0

第二种await()方法

package cn.tedu.concurrent2;


import java.util.concurrent.*;

public class CyclicBarrierDemo1 {

    public static void main(String[] args) {

        CountDownLatch latch = new CountDownLatch(1);

        CyclicBarrier barrier = new CyclicBarrier(10);


        Thread caipan = new Thread(new CaiPan(latch));

        caipan.start();


        for (int i = 0 ; i<10 ;i++){

            if(i<8){

                Thread sport =  new Thread(new SportMan(barrier));
                sport.start();
            }else{

                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Thread sport2 = new Thread(new SportMan(barrier));
                sport2.start();
            }

        }

    }





}

class SportMan implements Runnable{
    CyclicBarrier barrier = null;

    public SportMan(CyclicBarrier barrier) {
        this.barrier = barrier;
    }

    @Override
    public void run() {

        System.out.println(Thread.currentThread().getName()+"选手到达起跑线");
        try {
            barrier.await(2000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName()+"冲出了起跑线");

    }
}
class CaiPan implements  Runnable{
    CountDownLatch latch = null;

    public CaiPan(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        System.out.println("比赛开始");
        latch.countDown();
    }
}

执行结果

比赛开始
Thread-4选手到达起跑线
Thread-7选手到达起跑线
Thread-2选手到达起跑线
Thread-6选手到达起跑线
Thread-5选手到达起跑线
Thread-3选手到达起跑线
Thread-8选手到达起跑线
Thread-1选手到达起跑线
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
	at cn.tedu.concurrent2.SportMan.run(CyclicBarrierDemo1.java:59)
	at java.lang.Thread.run(Thread.java:745)
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
	at cn.tedu.concurrent2.SportMan.run(CyclicBarrierDemo1.java:59)
	at java.lang.Thread.run(Thread.java:745)
java.util.concurrent.TimeoutException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:257)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
	at cn.tedu.concurrent2.SportMan.run(CyclicBarrierDemo1.java:59)
	at java.lang.Thread.run(Thread.java:745)
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
	at cn.tedu.concurrent2.SportMan.run(CyclicBarrierDemo1.java:59)
	at java.lang.Thread.run(Thread.java:745)
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
	at cn.tedu.concurrent2.SportMan.run(CyclicBarrierDemo1.java:59)
	at java.lang.Thread.run(Thread.java:745)
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
	at cn.tedu.concurrent2.SportMan.run(CyclicBarrierDemo1.java:59)
	at java.lang.Thread.run(Thread.java:745)
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
	at cn.tedu.concurrent2.SportMan.run(CyclicBarrierDemo1.java:59)
	at java.lang.Thread.run(Thread.java:745)
Thread-7冲出了起跑线
Thread-1冲出了起跑线
Thread-4冲出了起跑线
Thread-3冲出了起跑线
Thread-8冲出了起跑线
Thread-2冲出了起跑线
Thread-5冲出了起跑线
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
	at cn.tedu.concurrent2.SportMan.run(CyclicBarrierDemo1.java:59)
	at java.lang.Thread.run(Thread.java:745)
Thread-6冲出了起跑线
Thread-9选手到达起跑线
Thread-9冲出了起跑线
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
	at cn.tedu.concurrent2.SportMan.run(CyclicBarrierDemo1.java:59)
	at java.lang.Thread.run(Thread.java:745)
Thread-10选手到达起跑线
Thread-10冲出了起跑线
java.util.concurrent.BrokenBarrierException
	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:207)
	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
	at cn.tedu.concurrent2.SportMan.run(CyclicBarrierDemo1.java:59)
	at java.lang.Thread.run(Thread.java:745)

Process finished with exit code 0

CycliBarrier重用案例


比赛开始
Thread-1选手到达起跑线
Thread-2选手到达起跑线
Thread-4选手到达起跑线
Thread-8选手到达起跑线
Thread-6选手到达起跑线
Thread-10选手到达起跑线
Thread-5选手到达起跑线
Thread-9选手到达起跑线
Thread-3选手到达起跑线
Thread-7选手到达起跑线
Thread-7冲出了起跑线
Thread-1冲出了起跑线
Thread-2冲出了起跑线
Thread-8冲出了起跑线
Thread-4冲出了起跑线
Thread-6冲出了起跑线
Thread-10冲出了起跑线
Thread-5冲出了起跑线
Thread-9冲出了起跑线
Thread-3冲出了起跑线
CyclicBarrier重用
Thread-11选手到达起跑线
Thread-12选手到达起跑线
Thread-13选手到达起跑线
Thread-16选手到达起跑线
Thread-14选手到达起跑线
Thread-15选手到达起跑线
Thread-18选手到达起跑线
Thread-19选手到达起跑线
Thread-17选手到达起跑线
Thread-20选手到达起跑线
Thread-20冲出了起跑线
Thread-11冲出了起跑线
Thread-12冲出了起跑线
Thread-16冲出了起跑线
Thread-18冲出了起跑线
Thread-17冲出了起跑线
Thread-13冲出了起跑线
Thread-19冲出了起跑线
Thread-15冲出了起跑线
Thread-14冲出了起跑线

Process finished with exit code 0

Semaphore用法

字面意思为信号量,Semaphore可以控制同时访问的线程的个数,通过acquire()获取一个许可,如果没有就等待,而release()释放一个许可。

Semaphore类位于java.util.concurrent包下

提供了2个构造器

public Semaphore(int permits) {          //参数permits表示许可数目,即同时可以允许多少线程进行访问
    sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {    //这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可
    sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
}

重要方法

public void acquire() throws InterruptedException {  }     //获取一个许可
public void acquire(int permits) throws InterruptedException { }    //获取permits个许可
public void release() { }          //释放一个许可
public void release(int permits) { }    //释放permits个许可

acquire()用来获取一个许可,如果没有许可能获取,则会一直等待,直到获得许可。

release()用来释放许可,在释放许可之前必须先获得许可。
上边的四个方法都会被阻塞

如果想立即得到执行结果,可以使用下面几个方法

public boolean tryAcquire() { };    //尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { };  //尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
public boolean tryAcquire(int permits) { }; //尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false

可以通过avaiablePermits()方法得到可用的许可数目

案例:

模拟餐馆吃饭

package cn.tedu.concurrent2;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {


    public static void main(String[] args) {

        Semaphore s = new Semaphore(10);
        for(int i = 0 ; i<20 ;i++){
            Restaurant res = new Restaurant(s);
            res.start();
        }

    }
}

class Restaurant extends Thread{

    Semaphore s = null;

    public Restaurant(Semaphore s) {
        this.s = s;
    }

    @Override
    public void run() {

        try {
            //获取一个许可
            s.acquire(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("一位客人占了一个座位");

        try {
            TimeUnit.SECONDS.sleep((long) (10*Math.random()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        s.release(1);
        System.out.println("一位客人起身离开了");

    }
}

运行结果

一位客人占了一个座位
一位客人占了一个座位
一位客人占了一个座位
一位客人占了一个座位
一位客人占了一个座位
一位客人占了一个座位
一位客人占了一个座位
一位客人占了一个座位
一位客人占了一个座位
一位客人起身离开了
一位客人占了一个座位
一位客人占了一个座位
一位客人起身离开了
一位客人占了一个座位
一位客人起身离开了
一位客人占了一个座位
一位客人起身离开了
一位客人占了一个座位
一位客人起身离开了
一位客人占了一个座位
一位客人占了一个座位
一位客人占了一个座位
一位客人起身离开了
一位客人占了一个座位
一位客人起身离开了
一位客人起身离开了
一位客人占了一个座位
一位客人起身离开了
一位客人起身离开了
一位客人起身离开了
一位客人占了一个座位
一位客人起身离开了
一位客人起身离开了
一位客人起身离开了
一位客人起身离开了
一位客人起身离开了

Process finished with exit code -1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

壹氿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值