Android进阶——多线程系列之Semaphore、CyclicBarrier、CountDownLatch

前言

今天向大家介绍的是多线程开发中的一些辅助类,他们的作用无非就是帮助我们让多个线程按照我们想要的执行顺序来执行。如果我们按照文字来理解Semaphore、CyclicBarrier、CountDownLatch可能会有点难度,如果看完实例再来看文字会恍然大悟。不用担心,今天带领大家用生活例子来理解这三个类,废话不多说,开车啦

Semaphore

Semaphore是一个计数信号量。信号量中维护着一个信号量许可集,线程可以通过调用acquire()来获取信号量的许可。当信号量被许可时,线程可以向下执行,否则线程等待。同时,线程也可以通过release()来释放它的信号量。Semaphore简单的可以理解为一张许可证

一、饭堂打饭

public class SemaphoreActivity extends AppCompatActivity {

    //线程数
    private static final int THREAD_SIZE = 5;
    private Semaphore semaphore;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_semaphore);

        semaphore = new Semaphore(3);

        for (int i = 0; i < THREAD_SIZE; i++) {
            new CanteenThread().start();
        }
    }

    private class CanteenThread extends Thread {
        @Override
        public void run() {
            try {
                //买到粮票
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName() + "号买到粮票");
                //模拟食堂打饭中
                System.out.println(Thread.currentThread().getName() + "号窗口开始打饭");
                Thread.sleep(2000);
                //提交粮票
                semaphore.release();
                System.out.println(Thread.currentThread().getName() + "号提交粮票");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

这里模拟了学生到食堂打饭的过程:买到粮票->打饭->提交粮票。其中semaphore代表食堂只有三张粮票的买卖,也可以理解为只有三个打饭窗口。THREAD_SIZE代表有五个学生线程同时打饭。下面通过打印信息来查看执行过程

Thread-636号买到粮票
Thread-636号窗口开始打饭
Thread-632号买到粮票
Thread-632号窗口开始打饭
Thread-635号买到粮票
Thread-635号窗口开始打饭

......隔了2秒后

Thread-636号提交粮票
Thread-633号买到粮票
Thread-633号窗口开始打饭
Thread-632号提交粮票
Thread-634号买到粮票
Thread-634号窗口开始打饭
Thread-635号提交粮票
Thread-633号提交粮票
Thread-634号提交粮票

学生们打饭还是井然有序的,打完就撤,留给下一位。Semaphore就是一张通行证,灵活使用它,你就能指挥线程

二、打印机

public class PrinterActivity extends AppCompatActivity {

    //线程数
    private static final int THREAD_SIZE = 5;
    private Semaphore semaphore;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_printer);

        semaphore = new Semaphore(1);

        for (int i = 0; i < THREAD_SIZE; i++) {
            new PrinterThread().start();
        }
    }

    private class PrinterThread extends Thread {
        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName() + "进入打印");
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "打印中...");
                System.out.println(Thread.currentThread().getName() + "退出打印");
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

这里模拟五个人抢占一部打印机的场景。下面通过打印信息来查看执行过程

Thread-231进入打印
Thread-231打印中...
Thread-231退出打印
Thread-229进入打印
Thread-229打印中...
Thread-229退出打印
Thread-228进入打印
Thread-228打印中...
Thread-228退出打印
Thread-230进入打印
Thread-230打印中...
Thread-230退出打印
Thread-227进入打印
Thread-227打印中...
Thread-227退出打印

CyclicBarrier

CyclicBarrier是一个同步辅助类,可以让正在运行中的线程与其他线程在某一公共时刻进行同步。CyclicBarrier简单理解为闸门,当我们达到某一目标时,闸门即可打开。

一、短跑比赛

public class MatchActivity extends AppCompatActivity {

    //线程数
    private static final int THREAD_SIZE = 5;
    private CyclicBarrier cb;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_match);

        cb = new CyclicBarrier(THREAD_SIZE, new Runnable() {
            @Override
            public void run() {
                //裁判
                System.out.println("参赛者" + cb.getParties() + "个全部准备完毕 --> 各就各位,预备跑");
            }
        });

        for (int i = 0; i < THREAD_SIZE; i++) {
            new AthleteThread().start();
        }

        System.out.println("主线程不用等待,继续执行");
    }

    private class AthleteThread extends Thread {
        @Override
        public void run() {
            try {
                //运动员
                System.out.println(Thread.currentThread().getName() + "号选手准备好了");
                cb.await();
                System.out.println(Thread.currentThread().getName() + "跑,跑,跑");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

这里模拟短跑比赛的过程:全部运动员准备->裁判一声令下->全部运动员开跑。其中THREAD_SIZE代表五名运动员,CyclicBarrier代表裁判。下面通过打印信息来查看这场比赛

Thread-682号选手准备好了
Thread-683号选手准备好了
Thread-684号选手准备好了
主线程不用等待,继续执行
Thread-686号选手准备好了
Thread-685号选手准备好了
参赛者5个全部准备完毕 --> 各就各位,预备跑
Thread-685跑,跑,跑
Thread-682跑,跑,跑
Thread-683跑,跑,跑
Thread-684跑,跑,跑
Thread-686跑,跑,跑

全部运动员们是在准备好之后开始起跑,这也就是我们所说的线程同步。它的特点是主线程不用等待,继续执行。

二、门禁系统

public class DoorActivity extends AppCompatActivity {

    //线程数
    private static final int THREAD_SIZE = 10;
    private CyclicBarrier cb;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_door);

        cb = new CyclicBarrier(THREAD_SIZE, new Runnable() {
            @Override
            public void run() {
                System.out.println("人到齐了 --> 开门");
            }
        });

        for (int i = 0; i < THREAD_SIZE; i++) {
            new PeopleThread().start();
        }

        System.out.println("主线程不用等待,继续执行");
    }

    private class PeopleThread extends Thread {
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread().getName() + "已刷卡 --> 等待开门回家");
                cb.await();
                System.out.println(Thread.currentThread().getName() + "放学回家");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

这里模拟十个学生放学回家的场景。下面通过打印信息来查看执行过程

Thread-268已刷卡 --> 等待开门回家
Thread-267已刷卡 --> 等待开门回家
主线程不用等待,继续执行
Thread-271已刷卡 --> 等待开门回家
Thread-272已刷卡 --> 等待开门回家
Thread-269已刷卡 --> 等待开门回家
Thread-270已刷卡 --> 等待开门回家
Thread-274已刷卡 --> 等待开门回家
Thread-273已刷卡 --> 等待开门回家
Thread-275已刷卡 --> 等待开门回家
Thread-276已刷卡 --> 等待开门回家
人到齐了 --> 开门
Thread-276放学回家
Thread-268放学回家
Thread-267放学回家
Thread-271放学回家
Thread-272放学回家
Thread-269放学回家
Thread-270放学回家
Thread-274放学回家
Thread-273放学回家
Thread-275放学回家

CountDownLatch

CountDownLatch也是一个同步辅助类,它可以设置一个或多个线程同时等待,直到条件被满足后,继续执行。

一、面试

public class CountDownLatchActivity extends AppCompatActivity {

    //线程数
    private static final int THREAD_SIZE = 5;
    private CountDownLatch cdl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_count_down_latch);

        try {
            cdl = new CountDownLatch(THREAD_SIZE);

            for (int i = 0; i < THREAD_SIZE; i++) {
                new IntervieweeThread().start();
            }

            //开始面试
            System.out.println("主线程需要等待");
            cdl.await();
            System.out.println("主线程继续执行 --> 面试会议结束了");

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

    private class IntervieweeThread extends Thread {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "号面试者,开始面试");
            cdl.countDown();
            System.out.println(Thread.currentThread().getName() + "面试中...面试成功");
        }
    }
}

这里模拟面试的过程:面试者进来->面试完成->面试者进来->面试完成的循环过程。其中THREAD_SIZE代表五名面试者,CountDownLatch代表面试这场会议。下面通过打印信息来查看面试过程

Thread-757号面试者,开始面试
Thread-757面试中...面试成功
Thread-758号面试者,开始面试
Thread-758面试中...面试成功
Thread-759号面试者,开始面试
Thread-759面试中...面试成功
主线程需要等待
Thread-760号面试者,开始面试
Thread-760面试中...面试成功
Thread-761号面试者,开始面试
Thread-761面试中...面试成功
主线程继续执行 --> 面试会议结束了

面试者是一个一个执行的,它的特点是可以让主线程进入等待状态,直到我们规定的五名面试者完成后才继续执行。

二、家人团圆饭

public class FamilyActivity extends AppCompatActivity {

    private CountDownLatch cdl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_family);

        cdl = new CountDownLatch(3);

        new Thread() {
            public void run() {
                fatherToRes();
                cdl.countDown();
            }
        }.start();

        new Thread() {
            public void run() {
                motherToRes();
                cdl.countDown();
            }
        }.start();

        new Thread() {
            public void run() {
                meToRes();
                cdl.countDown();
            }
        }.start();

        try {
            cdl.await();
            togetherToEat();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void fatherToRes() {
        System.out.println("爸爸步行去饭店需要3小时。");
    }

    public static void motherToRes() {
        System.out.println("妈妈挤公交去饭店需要2小时。");
    }

    public static void meToRes() {
        System.out.println("我乘地铁去饭店需要1小时。");
    }

    public static void togetherToEat() {
        System.out.println("一家人到齐了,开始吃饭");
    }
}

这里模拟一家三口回家吃团圆饭的场景,下面通过打印信息来查看执行过程

妈妈挤公交去饭店需要2小时。
爸爸步行去饭店需要3小时。
我乘地铁去饭店需要1小时。
一家人到齐了,开始吃饭

总结

1、归总

  1. Semaphore属于有上限的线程容器,当容器满的时候,不会再往容器中增加新的线程。比如:打印机
  2. CyclicBarrier属于等待线程容器,只有当容器的线程数满了后,才会执行所有线程,主线程不用等待(子线程需要等待)
  3. CountDownLatch属于等待线程容器,只有当容器的线程数满了后,才会执行所有线程,主线程需要等待(子线程不需要等待)

2、区别

Semaphore和CyclicBarrier和CountDownLatch区别在于:

  1. Semaphore容器里的线程从加入的时刻就会开始执行
  2. CyclicBarrier和CountDownLatch容器里的线程只有容器满了后才统一执行

CyclicBarrier和CountDownLatch区别在于:

  1. CyclicBarrier主线程不用等待,CountDownLatch主线程需要等待
  2. CyclicBarrier的计数器可以被重置后使用,CountDownLatch的计数器无法被重置
  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

许英俊潇洒

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

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

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

打赏作者

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

抵扣说明:

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

余额充值