CountDownLatch、CyclicBarrier、Semaphore、Exchanger详解

文章介绍了Java并发编程中的四种工具类:CountDownLatch用于线程等待,CyclicBarrier实现线程同步,Semaphore控制并发线程数量,Exchanger用于线程间数据交换。每个工具类都有其特定的使用场景和示例代码演示。

目录

一. CountDownLatch

二. CyclicBarrier

三. Semaphore

四. Exchanger


一. CountDownLatch

CountDownLatch 是 JDK5 之后加入的一种并发流程控制工具,它在 java.util.concurrent 包下,是能使一组线程等另一组线程都跑完了再继续跑。

public static void main(String[] args) throws InterruptedException {
    CountDownLatch countDownLatch = new CountDownLatch(5);

    for (int i = 1; i <= 5; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程" + 
                              Thread.currentThread().getName() + "开始运行");
                countDownLatch.countDown();
            }
        }, "t" + i).start();
    }

    countDownLatch.await();
    System.out.println("主线程最后执行-------------");
}

运行结果:

线程t1开始运行
线程t4开始运行
线程t2开始运行
线程t3开始运行
线程t6开始运行
主线程最后执行-------------

从结果可以得出,CountDownLatch可以控制线程执行先后,也就是调用了CountDownLatch.await()的线程直到countDown计数器为0后才可以执行。但是现在更喜欢用CompletableFuture来控制线程运行顺序

二. CyclicBarrier

CyclicBarrier的字面意思是可循环的(Cyclic)的使用屏障(Barrier)。它主要做的事情是让一组线程到达一个屏障点(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障是通过CyclicBarrier的await方法实现的。

CyclicBarrier可以使一定数量的线程反复地在屏障位置处汇集。当线程到达屏障位置时将调用await方法,这个方法将阻塞直到所有线程都到达屏障位置。如果所有线程都到达屏障位置,那么屏障将打开,此时所有的线程都将被释放。

public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("开始吃饭");
            }
        }));

        for (int i = 1; i <= 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName() + 
                                   "进入餐厅");
                        // 被阻塞,等待所有的线程都进入餐厅
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            }, "t"+ i).start();
        }
    }
}

运行结果:

t1进入餐厅
t4进入餐厅
t5进入餐厅
t3进入餐厅
t2进入餐厅
开始吃饭

从结果可以看出,当所有的线程都调用了 await方法,cyclicBarrier构造方法的线程才会执行,同时所有调用await方法的线程同时被唤醒,继续执行下面的操作

注意 :

CyclicBarrier的构造方法,可以传入一个实现了Runnable接口的类,表示,当所有的线程都调用了await,该线程才会被被执行。
CyclicBarrier的构造方法也可以只传入一个int类型的数字,表示当所有的线程(传入的参数数字的线程)都调用了await,所有的调用了await方法的线程都会被唤醒,继续执行下面的代码。
 

三. Semaphore

字面意思为 信号量,Semaphore能够控同时访问的线程个数,经过 acquire() 获取一个许可,若是没有就等待,而 release() 释放一个许可。

public static void main(String[] args) {
	// 表示3个车位
	Semaphore semaphore = new Semaphore(3);

	// 创建5个线程,一个线程代表一辆车
	for (int i = 1; i <= 5; i++) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					// 获得许可,在获取许可前该线程将一直阻塞在此,表示抢占车位
					semaphore.acquire();
					System.out.println(Thread.currentThread().getName() + 
                          " 占用了车位");
					Thread.sleep(3000);
					System.out.println(Thread.currentThread().getName() + 
                          " 停车3秒离开车位");
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					// 表示释放一个许可,表示离开车位
					semaphore.release();
				}
			}
		}, "t"+i).start();
	}
}

运行结果:

t1 占用了车位
t2 占用了车位
t3 占用了车位
t1 停车3秒离开车位
t2 停车3秒离开车位
t3 停车3秒离开车位
t4 占用了车位
t5 占用了车位
t4 停车3秒离开车位
t5 停车3秒离开车位

从运行结果可知,同时只能有3个线程(即车)进入停车,离开一辆车才能进来一辆车。

即semaphore.acquire()和semaphore.release()包起来的代码块,同时只能有3个线程进来,其他线程进来必须等待里面的线程执行完释放了资源,才能进入。

semaphore和synchronize的区别:
semaphore:可以控制多个线程进入,也可以是一个线程进入代码块
synchronize:只能控制一个线程计入代码块

总结:
Semaphore主要用于控制当前活动线程数目,就如同停车场系统一般,而Semaphore则相当于看守的人,用于控制总共允许停车的停车位的个数,而对于每辆车来说就如同一个线程,线程需要通过acquire()方法获取许可,而release()释放许可。如果许可数达到最大活动数,那么调用acquire()之后,便进入等待队列,等待已获得许可的线程释放许可,从而使得多线程能够合理的运行。

四. Exchanger

Exchanger是java 5引入的并发类,Exchanger顾名思义就是用来做交换的。这里主要是两个线程之间交换持有的对象。当Exchanger在一个线程中调用exchange方法之后,会等待另外的线程调用同样的exchange方法。

两个线程都调用exchange方法之后,传入的参数就会交换。

具体示例:

@Data
public class Book {
    private String name;
}

@Slf4j
public class ExchangerOne implements Runnable{
    Exchanger<Book> ex;

    ExchangerOne(Exchanger<Book> ex){
      this.ex = ex;
    }

    @Override
    public void run() {
        Book book= new Book();
        book.setName("book one");

        try {
            Book exhangeBook = ex.exchange(book);
            log.info(exhangeBook.getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

@Slf4j
public class ExchangerTwo implements Runnable {
    Exchanger<Book> ex;

    ExchangerTwo(Exchanger<Book> ex){
      this.ex = ex;
    }

    @Override
    public void run() {
        Book book = new Book();
        book.setName("book two");

        try {
            CustBook exhangeBook = ex.exchange(book);
            log.info(exhangeBook.getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ExchangerUsage {
    public static void main(String[] args) {
        Exchanger<Book> exchanger = new Exchanger<>();
        // Starting two threads
        new Thread(new ExchangerOne(exchanger)).start();
        new Thread(new ExchangerTwo(exchanger)).start();
    }
}

运行结果:

22:14:09.069 [Thread-1] INFO com.lm.mrluo735.ExchangerTwo - book one
22:14:09.073 [Thread-0] INFO com.lm.mrluo735.ExchangerOne - book two

可以看到对象已经被交换了。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

流华追梦

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

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

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

打赏作者

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

抵扣说明:

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

余额充值