Java多线程(7)

Java多线程(7)

CountDownLatch

CountDownLatch是一个同步辅助工具,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成

构造函数


CountDownLatch(int count)
构造一个以给定计数 CountDownLatch CountDownLatch。

方法


void	await()
导致当前线程等到锁存器计数到零,除非线程是 interrupted 


boolean	await(long timeout, TimeUnit unit)
使当前线程等待直到锁存器计数到零为止,除非线程为 interrupted或指定的等待时间过去

void	countDown()
减少锁存器的计数,如果计数达到零,释放所有等待的线程

long	getCount()
返回当前计数

String	toString()
返回一个标识此锁存器的字符串及其状态

先上个例子:

public class T1 {

    private static volatile CountDownLatch countDownLatch = new CountDownLatch(2);

    public static void main(String[] args) throws InterruptedException {

        Thread thread_1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);

                }catch (InterruptedException e){
                    e.printStackTrace();
                } finally {
                    countDownLatch.countDown();
                }
                System.out.println("one is over");

            }
        });


        Thread thread_2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e){
                    e.printStackTrace();
                } finally {
                    countDownLatch.countDown();
                }

                System.out.println("two is over");
            }
        });


        thread_1.start();
        thread_2.start();

        System.out.println("waiting all thread over.......");

        countDownLatch.await();

        System.out.println("over");


    }
}


通过例子可以看到:

CountDownLatch通过使用线程数初始化计数器来工作,每次线程完成执行时,该计数器都会递减。当count达到零时,表示所有线程都已完成执行,并且线程等待锁存器恢复执行。

模拟一个跑步比赛:

public class MarathonSimulator {

    public static void main(String[] args) throws Exception{
        ExecutorService executorService = Executors.newCachedThreadPool();
        int personCount = 3;
        final CountDownLatch prepare = new CountDownLatch(personCount);
        final CountDownLatch start = new CountDownLatch(1);
        final CountDownLatch end = new CountDownLatch(personCount);
        final Queue<String> queue = new ConcurrentLinkedQueue<>();
        if(personCount < 3){
            System.out.println("参赛人数必须 > 2");
            return;
        }
        for(int i=1; i<=personCount; i++){
            final int personNumber = i;
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    //模拟准备时间,使用随机数
                    try{
                        Thread.sleep(new Random(personNumber).nextInt(1000));
                    }catch(Exception e){}
                    System.out.println(personNumber+"号运动员准备完毕");
                    prepare.countDown();
                    try {
                        //等待开始跑的命令
                        start.await();
                    }catch (Exception e){}
                    System.out.println(personNumber+"号运动员开始跑");
                    long s = System.currentTimeMillis();
                    //模拟跑步时间,使用随机数
                    try{
                        Thread.sleep(new Random(personNumber).nextInt(10000));
                    }catch(Exception e){}
                    long c = System.currentTimeMillis() - s;
                    System.out.println(personNumber+"号运动员到达目的地,耗时:"+c+"毫秒");

                    end.countDown();
                    queue.add(personNumber+"号");
                }
            });
        }
        System.out.println("有"+personCount+"名运动员参加本次的马拉松比赛");
        System.out.println("教练在等待运动员准备...\n");
        prepare.await();
        System.out.println("\n所有运动员准备完毕,教练开始喊:预备 --- 跑!");
        start.countDown();
        System.out.println("\n教练在等待所有的运动员跑完...\n");
        end.await();

        System.out.println("\n所有运动员都跑完了,开始颁奖啦:");
        System.out.println("冠军是:"+queue.poll());
        System.out.println("亚军是:"+queue.poll());
        System.out.println("季军是:"+queue.poll());

        executorService.shutdown();
    }
}


CyclicBarrier

功能:同步辅助类,功能和CountDownLatch类似,用于同步线程状态,允许一组线程相互之间等待,达到一个共同点,再继续执行。

特点:可复用!当组内所有线程都到达某个执行点后,count参数会被重置,于是就可重用了。

构造函数


CyclicBarrier(int parties)
创建一个新的 CyclicBarrier ,当给定数量的线程(线程)等待它时,它将跳闸,并且当屏障跳闸时不执行预定义的动作

CyclicBarrier(int parties, Runnable barrierAction)
创建一个新的 CyclicBarrier ,当给定数量的线程(线程)等待时,它将跳闸,当屏障跳闸时执行给定的屏障动作,由最后一个进入屏障的线程执行

方法


int	await()
等待所有 parties已经在这个障碍上调用了 await 

int	await(long timeout, TimeUnit unit)
等待所有 parties已经在此屏障上调用 await ,或指定的等待时间过去

int	getNumberWaiting()
返回目前正在等待障碍的各方的数量

int	getParties()
返回旅行这个障碍所需的聚会数量

boolean	isBroken()
查询这个障碍是否处于破碎状态

void	reset()
将屏障重置为初始状态

先上例子:


public class T3 extends Thread{

    private CyclicBarrier cyclicBarrier;

    public T3(CyclicBarrier cyclicBarrier) {
        super();
        this.cyclicBarrier = cyclicBarrier;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(5000);
            System.out.println(Thread.currentThread().getName()
            +"   到了 "
            +System.currentTimeMillis());
            cyclicBarrier.await();
        }catch (InterruptedException e){
            e.printStackTrace();
        } catch (BrokenBarrierException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
            @Override
            public void run() {
                System.out.println("全部到了");
            }
        });

        T3[] t3s = new T3[5];

        for (int i = 0; i < t3s.length; i++) {
            t3s[i] = new T3(cyclicBarrier);
        }

        for (int i = 0; i < t3s.length; i++) {
            t3s[i].start();
        }
    }
}

CyclicBarrier类的计数器是可以重置的,接下来实现一个阶段性的比赛:


public class MyService {

    private CyclicBarrier cyclicBarrier;

    public MyService(CyclicBarrier cyclicBarrier) {
        this.cyclicBarrier = cyclicBarrier;
    }

    public void beginRun(){

        try {
            long val = (long) (Math.random()*1000);

            Thread.sleep(val);

            
//            getNumberWaiting方法的作用是获取有几个线程已经到达屏障点
            System.out.println(Thread.currentThread().getName()+"   "
            + System.currentTimeMillis() + "  开始跑第一阶段  "
            + (cyclicBarrier.getNumberWaiting() + 1));

            cyclicBarrier.await();

            System.out.println(Thread.currentThread().getName()+"   "
            + System.currentTimeMillis() + "   结束第一阶段的跑步  "
            + cyclicBarrier.getNumberWaiting());


            val = (long) (Math.random()*1000);

            Thread.sleep(val);

            System.out.println(Thread.currentThread().getName()+"    "
                    + System.currentTimeMillis() + "  开始跑第二阶段  "
                    + (cyclicBarrier.getNumberWaiting() + 1));

            cyclicBarrier.await();

            System.out.println(Thread.currentThread().getName()+"   "
                    + System.currentTimeMillis() + " 结束第二阶段的跑步  "
                    + cyclicBarrier.getNumberWaiting());

            System.out.println("");

        } catch (InterruptedException e){
            e.printStackTrace();
        } catch (BrokenBarrierException e){
            e.printStackTrace();
        }
    }
}


public class AThread extends Thread{

    private MyService myService;

    public AThread(MyService myService) {
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.beginRun();
    }
}

public class Test {


    public static void main(String[] args) {


        CyclicBarrier cyclicBarrier = new CyclicBarrier(2);

        MyService myService = new MyService(cyclicBarrier);

        AThread aThread1 = new AThread(myService);

        aThread1.setName("A");

        aThread1.start();

        AThread aThread2 = new AThread(myService);

        aThread2.setName("B");

        aThread2.start();

        AThread aThread3 = new AThread(myService);

        aThread3.setName("C");

        aThread3.start();

        AThread aThread4 = new AThread(myService);

        aThread4.setName("D");

        aThread4.start();

    }
}


CountDownLatch和CyclicBarrier对比测试

CountDown表示减法计数,Latch表示门闩的意思,计数为0的时候就可以打开门闩了。Cyclic Barrier表示循环的障碍物

两个类都含有这一个意思:对应的线程都完成工作之后再进行下一步动作,也就是大家都准备好之后再进行下一步。

然而两者最大的区别是,进行下一步动作的动作实施者是不一样的。这里的“动作实施者”有两种,一种是主线程(即执行main函数),另一种是执行任务的其他线程,后面叫这种线程为“其他线程”,区分于主线程。对于CountDownLatch,当计数为0的时候,下一步的动作实施者是main函数;对于CyclicBarrier,下一步动作实施者是“其他线程”。

对比:

public class CountDownLatchTest {

//    模拟了三个玩家,在三个玩家都准备好之后,游戏才能开始。

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(4);
        for(int i = 0; i < latch.getCount(); i++){
            new Thread(new MyThread(latch), "player"+i).start();
        }
        System.out.println("正在等待所有玩家准备好");
        latch.await();
        System.out.println("开始游戏");
    }

    private static class MyThread implements Runnable{
        private CountDownLatch latch ;

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

        @Override
        public void run() {
            try {
                Random rand = new Random();
                int randomNum = rand.nextInt((3000 - 1000) + 1) + 1000;//产生1000到3000之间的随机整数
                Thread.sleep(randomNum);
                System.out.println(Thread.currentThread().getName()+" 已经准备好了, 所使用的时间为 "+((double)randomNum/1000)+"s");
                latch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }


}


public class CyclicBarrierTest {


//    对于CyclicBarrier,假设有一家公司要全体员工进行团建活动,活动内容为翻越三个障碍物,每一个人翻越障碍物所用的时间是不一样的。但是公司要求所有人在翻越当前障碍物之后再开始翻越下一个障碍物,也就是所有人翻越第一个障碍物之后,才开始翻越第二个,以此类推。类比地,每一个员工都是一个“其他线程”。当所有人都翻越的所有的障碍物之后,程序才结束。而主线程可能早就结束了,这里我们不用管主线程。
//
//我们使用代码来模拟上面的过程。我们设置了三个员工和三个障碍物。可以看到所有的员工翻越了第一个障碍物之后才开始翻越第二个的,下面是运行结果:

    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(3);
        for(int i = 0; i < barrier.getParties(); i++){
            new Thread(new MyRunnable(barrier), "队友"+i).start();
        }
        System.out.println("main function is finished.");
    }


    private static class MyRunnable implements Runnable{
        private CyclicBarrier barrier;

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

        @Override
        public void run() {
            for(int i = 0; i < 3; i++) {
                try {
                    Random rand = new Random();
                    int randomNum = rand.nextInt((3000 - 1000) + 1) + 1000;//产生1000到3000之间的随机整数
                    Thread.sleep(randomNum);
                    System.out.println(Thread.currentThread().getName() + ", 通过了第"+i+"个障碍物, 使用了 "+((double)randomNum/1000)+"s");
                    this.barrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        }
    }


}



Semaphore

  • Semaphore是一个计数信号量。

  • 从概念上将,Semaphore包含一组许可证。

  • 如果有需要的话,每个acquire()方法都会阻塞,直到获取一个可用的许可证。

  • 每个release()方法都会释放持有许可证的线程,并且归还Semaphore一个可用的许可证。

  • 然而,实际上并没有真实的许可证对象供线程使用,Semaphore只是对可用的数量进行管理维护。

构造函数


Semaphore(int permits)
创建一个 Semaphore与给定数量的许可证和非公平公平设置


Semaphore(int permits, boolean fair)
创建一个 Semaphore与给定数量的许可证和给定的公平设置

方法



void	acquire()
从该信号量获取许可证,阻止直到可用,或线程为 interrupted 

void	acquire(int permits)
从该信号量获取给定数量的许可证,阻止直到所有可用,否则线程为 interrupted 

void	acquireUninterruptibly()
从这个信号灯获取许可证,阻止一个可用的

void	acquireUninterruptibly(int permits)
从该信号量获取给定数量的许可证,阻止直到所有可用

int	availablePermits()
返回此信号量中当前可用的许可数

int	drainPermits()
获取并返回所有可立即获得的许可证

protected Collection<Thread>	getQueuedThreads()
返回一个包含可能正在等待获取的线程的集合

int	getQueueLength()
返回等待获取的线程数的估计

boolean	hasQueuedThreads()
查询任何线程是否等待获取

boolean	isFair()
如果此信号量的公平设置为真,则返回 true 

protected void	reducePermits(int reduction)
缩小可用许可证的数量

void	release()
释放许可证,将其返回到信号量

void	release(int permits)
释放给定数量的许可证,将其返回到信号量

String	toString()
返回一个标识此信号量的字符串及其状态

boolean	tryAcquire()
从这个信号量获得许可证,只有在调用时可以使用该许可证

boolean	tryAcquire(int permits)
从这个信号量获取给定数量的许可证,只有在调用时全部可用

boolean	tryAcquire(int permits, long timeout, TimeUnit unit)
从该信号量获取给定数量的许可证,如果在给定的等待时间内全部可用,并且当前线程尚未 interrupted 

boolean	tryAcquire(long timeout, TimeUnit unit)
如果在给定的等待时间内可用,并且当前线程尚未 到达 interrupted,则从该信号量获取许可


先上例子:


public class Service {

    private Semaphore semaphore = new Semaphore(1);

    public void test(){

        try {
            semaphore.acquire();

            System.out.println(Thread.currentThread().getName()
            +"  开始  " + System.currentTimeMillis());

            Thread.sleep(5000);

            System.out.println(Thread.currentThread().getName()
            +" 结束 " + System.currentTimeMillis());

            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class A extends Thread{

    private Service service;

    public A(Service service) {
        this.service = service;
    }

    @Override
    public void run() {

        service.test();
    }
}


public class B extends Thread{


    private Service service;

    public B(Service service) {
        this.service = service;
    }

    @Override
    public void run() {

        service.test();
    }
}


public class C extends Thread{

    private Service service;

    public C(Service service) {
        this.service = service;
    }

    @Override
    public void run() {

        service.test();
    }
}



public class Run {

    public static void main(String[] args) {


        Service service = new Service();

        A a = new A(service);

        a.setName("A");

        B b = new B(service);

        b.setName("B");

        C c = new C(service);

        c.setName("C");

        a.start();
        b.start();
        c.start();




    }
}

关键语句:private Semaphore semaphore = new Semaphore(1);

定义最多允许一个线程执行acquire()与release()之间的代码,所以打印结果就是三个线程同步执行

使用例子:


public class SemaphoreThread extends Thread{

    private String name;
    private Semaphore semaphore;

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

    @Override
    public void run() {
        if (semaphore.availablePermits() <= 0) {
            System.out.println(name + "等位中。。。");
        }
        try {
            semaphore.acquire();
            System.out.println(name + "开始就餐了。。");
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name + "吃完了。。");
        semaphore.release();
    }

    public static void main(String[] args) {

        Semaphore semaphore = new Semaphore(2);
        for (int i = 1; i <= 3; i++) {
            new SemaphoreThread("第" + i + "个人", semaphore).start();
        }

    }
}

发布了189 篇原创文章 · 获赞 58 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/Coder_py/article/details/104050072