CountDownLatch、CyclicBarrier、Semaphore

DownCountLatch 闭锁、多线程计数器

DownCountLatch :闭锁、多线程计数器, 允许一个或多个线程,等待其他一组线程完成操作,再继续执行。
实例化DownCountLatch时候传入计数值,当一个线程调用DownCountLatch的await方法的时候,这个线程会处于等待状态,其他线程任务中调用countDown方法,每次count减一,当count减到0为止,这个线程才会继续运行下去。

public CountDownLatch(int count) //构造参数count为计数值,只有当count等于0的时候,等待状态的线程才能运行下去。

public void await() //调用await()方法的线程会被挂起,直到count值为0才继续执行
public boolean await(long timeout, TimeUnit unit) //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行

public void countDown() //将count值减1

例子:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {

    public static void main(String[] args) {

        CountDownLatch countDownLatch = new CountDownLatch(10); // 1. 闭锁相当于一个计数器 // 10
        MyRunnable myRunnable = new MyRunnable(countDownLatch);
        long start = System.currentTimeMillis();

        for (int i = 0; i <10; i++) {
            new Thread(myRunnable).start();
        }

        // 3. 等待 计数器完成倒数操作, 才往下执行
        try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); }
        long end = System.currentTimeMillis();

        Console.log("=====多线程,执行完成时间====>{}",end-start);
    }
}


class MyRunnable  implements Runnable {
    private CountDownLatch latch;

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

    @Override
    public void run() {
        synchronized (MyRunnable.class) {

            try {
                for (int i =0; i<10000; i++){
                    Console.log(i);
                }
            } finally {
                // 2. 每次执行线程任务, 计数器-1
                latch.countDown();
            }
        }

    }
}
CyclicBarrier 同步屏障

CyclicBarrier:一组线程,每个线程执行到的约定进度,就等待其他线程,等全部达到约定进度,才能继续执行各自线程任务的后续代码。
在实例化CyclicBarrier的时候会给它传一个int类型的参数N,只有N个线程都运行到CyclicBarrier的await方法的时候,这些线程才能继续运行下去。也就是说当前面N-1个线程运行到await方法的时候,N-1个线程都是等待的状态,只有当第N个线程运行await方法的时候,这N个线程才是处于就绪状态。

构造方法
public CyclicBarrier(int parties, Runnable barrierAction)
public CyclicBarrier(int parties)
方法
public int await()
public int await(long timeout, TimeUnit unit)

例子:

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

public class CyclicBarrierTest {
	public static void main(String[] args) throws Exception{

		CyclicBarrier cyclicBarrier = new CyclicBarrier(6,new Runnable(){
			public void run(){
				System.out.println("......................");
			}
		});
 
		for(int i=0;i<6;i++){
			RunnableTask runnableTask = new RunnableTask(cyclicBarrier);
			Thread thread = new Thread(runnableTask,i+"");
			thread.start();
		}
	}  
 
}
 
 
class RunnableTask implements Runnable {
	CyclicBarrier cyclicBarrier;
	public RunnableDemoTwo(CyclicBarrier cyclicBarrier){
		this.cyclicBarrier = cyclicBarrier;
	}
	@Override
	public void run(){
		
		try {			
			Thread.sleep(5000);
			System.out.println(Thread.currentThread().getName()+"========before");
			cyclicBarrier.await();
			System.out.println(Thread.currentThread().getName()+"========after");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
Semaphore 信号量

一个信号量相当于持有一些许可(permits),线程可以调用Semaphore对象的acquire()方法获取一个许可,调用release()来归还一个许可。
Semaphore:在实例化Semaphore的时候,会传一个int类型的参数count,count就代表着Semaphore的信号池中有count个信号量,当一个线程调用Semaphore的acquire()或acquire(n)方法的时候就是获取1个或n个信号量,Semaphore的信号池就会减去这个线程获取到的信号量,当其他线程再调用acquire(m)方法获取信号量的时候,Semaphore信号池的中的信号量个数大于m,这个线程就可以继续运行,当信号池中的信号量小于m,这个线程就会处于等待状态,等待拥有信号量的线程调用release(n)方法释放自己所拥有的的信号,等信号池的信号量大于等于索要的信号量时,这个线程才会继续运行下去。

构造方法
public Semaphore(int permits) 参数permits表示该信号量拥有的许可数量,即同时可以允许多少线程进行访问
public Semaphore(int permits, boolean fair) ` //参数fair表示获取许可的时候是否是公平的,如果是公平的那么,当有多个线程要获取许可时,会按照线程来的先后顺序分配许可,否则,线程获得许可的顺序是不定的。这里在jdk中讲到 “一般而言,非公平时候的吞吐量要高于公平锁”,这是为什么呢?附上链接中的一段话:

非公平锁性能高于公平锁性能的原因:在恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟。假设线程A持有一个锁,并且线程B请求这个锁。由于锁被A持有,因此B将被挂起。当A释放锁时,B将被唤醒,因此B会再次尝试获取这个锁。与此同时,如果线程C也请求这个锁,那么C很可能会在B被完全唤醒之前获得、使用以及释放这个锁。这样就是一种双赢的局面:B获得锁的时刻并没有推迟,C更早的获得了锁,并且吞吐量也提高了。当持有锁的时间相对较长或者请求锁的平均时间间隔较长,应该使用公平锁。在这些情况下,插队带来的吞吐量提升(当锁处于可用状态时,线程却还处于被唤醒的过程中)可能不会出现。

获取许可
可以使用acquire()、acquire(int)、tryAcquire()等去获取许可,其中int参数表示一次性要获取几个许可,默认为1个,acquire方法在没有许可的情况下,要获取许可的线程会阻塞,而tryAcquire()方法在没有许可的情况下会立即返回 false,要获取许可的线程不会阻塞,这与Lock类的lock()与tryLock()类似
public void acquire() //获取一个许可
public void acquire(int permits) //获取permits个许可
public boolean tryAcquire() //尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(long timeout, TimeUnit unit) //尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
public boolean tryAcquire(int permits) //尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) //尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false

释放许可
线程可调用 release()、release(int)来释放(归还)许可,注意一个线程调用release()之前并不要求一定要调用了acquire (There is no requirement that a thread that releases a permit must have acquired that permit by calling {@link #acquire})

public void release() //释放一个许可
public void release(int permits) //释放permits个许可

例子:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SemaPhoreTest {
    public static void main(String[] args) {
        // 线程池
        ExecutorService exec = Executors.newCachedThreadPool();
        // 只能5个线程同时访问
        final Semaphore semp = new Semaphore(5);
        // 模拟20个客户端访问
        for (int index = 0; index < 50; index++) {
            final int NO = index;
            Runnable run = new Runnable() {
                public void run() {
                    try {
                        // 获取许可
                        semp.acquire();
                        System.out.println("Accessing: " + NO);
                        Thread.sleep((long) (Math.random() * 6000));
                        // 访问完后,释放
                        semp.release();
                        //availablePermits()指的是当前信号灯库中有多少个可以被使用
                        System.out.println("-----------------" + semp.availablePermits()); 
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            exec.execute(run);
        }
        // 退出线程池
        exec.shutdown();
    }
}
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;
 

public class SemaphoreTest {
	public static void main(String[] args) {
		Semaphore semaphore = new Semaphore(10); // 一共10个信号量
		new Thread(new MyRunnable(semaphore,5), "线程1").start(); // 线程1 任务 需要 5个 信号量
		new Thread(new MyRunnable(semaphore,3), "线程2").start(); // 线程2 任务 需要 3个 信号量
		new Thread(new MyRunnable(semaphore,7), "线程3").start(); // 线程3 任务 需要 7个 信号量
	} 
 
}
 
 
class MyRunnable implements Runnable {
	Semaphore semaphore;
	int i;
	public MyRunnable(Semaphore semaphore,int i){
		this.semaphore = semaphore;
		this.i = i;
    }
    
	@Override
	public void run() {
		
		try {
            // 线程1,2,3运行到 semaphore.acquire(i)方法的时候,semaphore信号池的信号量是10
            // 线程1和线程2分别获取了5个和3个信号量,线程池的限号量还剩2个
            // 等线程3再去获取7个信号量的时候就会处于等待状态,
            // 线程3只有等待线程1和线程2运行到 semaphore.release(i)时,线程3才能获取到足够的信号量,才能继续运行。
			semaphore.acquire(i);	
			System.out.println(Thread.currentThread().getName()+", 获得 "+i+" 个信号量... do something");
			Thread.sleep(2000);		
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
             // 释放给定数目的许可,将其返回到信号量。
			semaphore.release(i);
			System.out.println(Thread.currentThread().getName()+", 释放 "+i+" 个信号量");
	    }
	}
}
CountDownLatch 是一个计数器,它可以让一个或多个线程等待其他线程执行完毕后再继续执行。它的主要方法是 countDown() 和 await(),其中 countDown() 用于计数减一,await() 用于等待计数器变为0。与 CountDownLatch 相比,CyclicBarrier 的主要区别在于它可以重复使用,而且所有线程必须同时到达栅栏处才能继续执行后续任务。CyclicBarrier 的重要方法是 await(),并且可以通过构造方法传入一个 Runnable,在所有线程都到达栅栏状态时优先执行该动作。CyclicBarrier 内部使用 ReentrantLock 和 Condition 实现等待和唤醒的功能,通过维护一个 count 变量来记录还有多少个线程没有到达栅栏处。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [CountDownLatchCyclicBarrier](https://blog.csdn.net/weixin_44442186/article/details/123985119)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [JUC多线程:CountDownLatchCyclicBarrierSemaphore同步器原理总结](https://blog.csdn.net/a745233700/article/details/120688546)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值