五、CountDownLatch、CyclicBarrier和Semaphore的比较

一、JMM模型与volatile详解
二、synchronized原理详解
三、AQS框架详解——AbstractQueuedSynchronizer
四、ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue和DelayQueue学习总结
五、CountDownLatch、CyclicBarrier和Semaphore的比较
java中interruptor理解与使用
Java线程的6种状态及切换
MESI协议:保证可见性,无法保证原子性

javadoc是这么描述它们的:

(1) CountDownLatch: A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
(2)CyclicBarrier : A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.

从javadoc的描述可以得出:
(1)
CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;
CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行。
Semaphore:可以设置一个数字,表示同时可以执行的线程数量。
(2)
对于CountDownLatch来说,重点是“一个线程(多个线程)等待”,而其他的N个线程在完成“某件事情”之后,可以终止,也可以等待。 而对于CyclicBarrier,重点是多个线程,在任意一个线程没有完成,所有的线程都必须互相等待,然后继续一起执行。
(3)
CountDownLatch是计数器,线程完成一个记录一个,只不过计数不是递增而是递减,而CyclicBarrier更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行。

一、CountDownLatch

可以使一个线程等待其他线程完成工作后再执行,例如zookeeper的分布式锁。

1、构造函数

 public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

count表示等待的线程数目,底层依然依赖AQS的state。

2、重要方法

public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

阻塞当前线程,直到count减小到0。

 public void countDown() {
        sync.releaseShared(1);
    }

count减小1。

3、CountDownLatch用法示例

举例:张三、李四、王五去餐馆吃饭,服务员需要等他们三个人到齐了之后才能开始;关键点是:一个服务员等待三个顾客

package com.ysy.countdownlatch;

import java.util.concurrent.CountDownLatch;

/**
 * @author 
 * @date 2020/6/17
 * CountDownLatch:等待其他几个线程把downlatch减小到0,再执行;
 * 张三、李四、王五去餐馆吃饭,服务员需要等他们三个人到齐了之后才能开始上菜。
 */
public class MainTest {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(3);
        /**
         * 三个人去餐馆吃饭
         * */
        createCustomerThread("张三", countDownLatch);
        createCustomerThread("李四", countDownLatch);
        createCustomerThread("王五", countDownLatch);

        Waitress waitress = new Waitress(countDownLatch);

        Thread thread = new Thread(waitress);

        thread.start();

    }

    private static void createCustomerThread(String name, CountDownLatch countDownLatch) {
        Customer customer = new Customer(countDownLatch, name);
        Thread thread = new Thread(customer);
        thread.start();
    }
}
package com.ysy.countdownlatch;

import java.util.concurrent.CountDownLatch;

/**
 * @author shanyangyang
 * @date 2020/6/17
 * Customer:
 * 去餐馆消费的人
 */
public class Customer implements Runnable {
    private CountDownLatch countDownLatch;
    /**
     * 餐馆消费者类
     */
    private String name;

    public Customer(CountDownLatch countDownLatch, String name) {
        this.countDownLatch = countDownLatch;
        this.name = name;
    }

    @Override public void run() {
        try {
            Thread.sleep((long)(Math.random() * 10000));
            System.out.println(name + "到达餐馆");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //数量减一
            countDownLatch.countDown();
        }
    }
}
package com.ysy.countdownlatch;

import java.util.concurrent.CountDownLatch;

/**
 * @author 
 * @date 2020/6/17
 * Waitress:
 * 餐馆服务员,等张三、李四、王五三个人到齐后开始上菜
 */
public class Waitress implements Runnable {
    private CountDownLatch countDownLatch;

    public Waitress(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override public void run() {
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("服务员开始上菜");
    }
}

CyclicBarrier

阻塞一定数量的线程,直到最后一个线程也到达屏障时,才打开屏障,所有的线程继续执行。

1、构造函数

 public CyclicBarrier(int parties) {
        this(parties, null);
    }
  public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

2、重要方法

 public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }
private int dowait(boolean timed, long nanos)
    throws InterruptedException, BrokenBarrierException,
           TimeoutException {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        final Generation g = generation;

        if (g.broken)
            throw new BrokenBarrierException();

        if (Thread.interrupted()) {
            breakBarrier();
            throw new InterruptedException();
        }

        int index = --count;
        if (index == 0) {  // tripped
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand;
                if (command != null)
                    command.run();
                ranAction = true;
                nextGeneration();
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier();
            }
        }

        // loop until tripped, broken, interrupted, or timed out
        for (;;) {
            try {
                if (!timed)
                    trip.await();
                else if (nanos > 0L)
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
                if (g == generation && ! g.broken) {
                    breakBarrier();
                    throw ie;
                } else {
                    // We're about to finish waiting even if we had not
                    // been interrupted, so this interrupt is deemed to
                    // "belong" to subsequent execution.
                    Thread.currentThread().interrupt();
                }
            }

            if (g.broken)
                throw new BrokenBarrierException();

            if (g != generation)
                return index;

            if (timed && nanos <= 0L) {
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
        lock.unlock();
    }
}

3、CyclicBarrier使用实例

例子:张三、李四、王五和杨六去餐馆吃饭,他们去餐馆之后相互等待对方,等人齐了才能开饭。

package com.company;

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;

/**
 * @author 
 */
public class Main {

    public static void main(String[] args) throws InterruptedException {
        //创建线程池,避免手工显示创建线程
        ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(4);


        CyclicBarrier cyclicBarrier = new CyclicBarrier(4);

	    PlayGame playGame1 = new PlayGame(cyclicBarrier,"张三");

        PlayGame playGame2 = new PlayGame(cyclicBarrier,"李四");

        PlayGame playGame3 = new PlayGame(cyclicBarrier,"王五");

        PlayGame playGame4 = new PlayGame(cyclicBarrier,"杨六");


        executorService.execute(playGame1);

        executorService.execute(playGame2);

        executorService.execute(playGame3);

        executorService.execute(playGame4);

        executorService.shutdown();

    }
}
package com.company;

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

/**
 * @author 
 * @date 2020/6/18
 */
public class PlayGame implements Runnable{
    private CyclicBarrier cyclicBarrier;
    private  String name;

    public PlayGame(CyclicBarrier cyclicBarrier,String name) {
        this.cyclicBarrier = cyclicBarrier;
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(name+"到达餐厅,等人齐了开始吃饭");
        try {
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        } 
        System.out.println("人齐了大家开始吃饭");
    }
}

在这里插入图片描述

CyclicBarrier设置超时时间

cyclicBarrier.await(1000, TimeUnit.MILLISECONDS);

CyclicBarrier可以重用

三、Semaphore

Semaphore是信号量的意思,可以控制访问特定资源的线程数量,底层依赖AQS中的状态state。

1、构造函数

public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

permits表示线程的数量;

 public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

为true时表示公平锁,为false时表示非公平锁;

2、重要方法

  public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

获取许可;

  public void release() {
        sync.releaseShared(1);
    }

释放锁

可以看出底层实现依然是依赖我们前面所说的AQS框架。

3、Semaphore用法实例

张三、李四和王五去吃饭,但是餐馆只有两个位置,第三个到的人需要等前面的人把位置让出来

package com.company;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.Semaphore;

public class Main {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2);

        ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(2);

        Customer customer1 = new Customer(semaphore,"张三");

        Customer customer2 = new Customer(semaphore,"李四");

        Customer customer3 = new Customer(semaphore,"王五");

        scheduledExecutorService.execute(customer1);

        scheduledExecutorService.execute(customer2);

        scheduledExecutorService.execute(customer3);

        scheduledExecutorService.shutdown();
    }
}
package com.company;

import java.util.concurrent.Semaphore;


/**
 * @author 
 * @date 2020/6/18
 */
public class Customer implements Runnable{
    private Semaphore semaphore;
    private String name;

    public Customer(Semaphore semaphore, String name) {
        this.semaphore = semaphore;
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(name+":先问一下餐馆有没有位置");
        try {
            semaphore.acquire();
            System.out.println(name+":有座位了,开始吃饭");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        semaphore.release();
        System.out.println(name+":我吃完了,把位置让出来");
    }
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值