前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >同步器

同步器

作者头像
搬砖俱乐部
发布2019-06-15 17:34:46
4570
发布2019-06-15 17:34:46
举报
文章被收录于专栏:BanzClubBanzClub

Java提供两种同步机制,一种是内置的synchronize,另外一种就是大名鼎鼎的AQS,基于AQS实现了很多同步器:倒数闩锁(CountDownLatch)、信号量(Semaphore)、可循环使用的屏障(CyclicBarrier)。

AQS内部维护着一个FIFO队列,该队列的算法实现了并发访问控制,每个节点代表一个请求线程,还包括线程的状态(等待、取消、阻塞等),另外AQS包括公平和非公平两种模式,公平就是严格遵循FIFO规则,非公平属于一种抢占模式,允许插队。AQS通过state属性值和thread是否是当前线程,来控制独占和共享模式。当然不同的实现state可以有不同的逻辑,

在ReentrantLock中,state值就代表线程获得锁的标识,state为0,没有线程获得锁,state大于0,已经有线程获得到了锁。

倒数闩锁

在CountDownLatch中,构造方法就是给state设置一个值,然后通过countDown,释放state数量,来实现线程倒计数的功能,当所有线程都释放完成之后,被阻塞的主线程被唤醒,继续执行,所以CountDownLatch就像一个会倒计数的锁。

CountDownLatch的使用场景可以概括为,在某几个工作完成后,才能做某项重要工作的场景,类似于做某事情需要的准备工作。

如:某对战型游戏里一局游戏开始准备工作(吃鸡):

代码语言:javascript
复制
/**
 * 初始游戏场景(可能包括各种资源)
 * 1、游戏地图
 * 2、道具初始
 * 3、网络资源
 * 4、100个队员建立连接
 * 5、其他资源
 */
private static final CountDownLatch countDownLatch = new CountDownLatch(5);
// 模拟
private static final ExecutorService pool = Executors.newFixedThreadPool(5);
public static void main(String[] args) {
    Runnable buildMap = () -> {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
        }
        System.out.println("游戏地图初始化结束!");
        countDownLatch.countDown();
    };
    Runnable buildProp = () -> {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
        }
        System.out.println("道具初始化结束!");
        countDownLatch.countDown();
    };
    Runnable buildNet = () -> {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
        }
        System.out.println("网络资源初始化结束!");
        countDownLatch.countDown();
    };
    Runnable players = () -> {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
        }
        System.out.println("100个队员连接已经建立!");
        countDownLatch.countDown();
    };
    Runnable otherResources = () -> {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
        }
        System.out.println("其他资源初始化结束!");
        countDownLatch.countDown();
    };
    pool.execute(buildMap);
    pool.execute(buildProp);
    pool.execute(buildNet);
    pool.execute(players);
    pool.execute(otherResources);
    try {
        countDownLatch.await();
    } catch (InterruptedException e) {
        System.out.println("异常");
    }
    pool.shutdown();
    System.out.println("游戏初始化结束,马上开始");
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {    }
    System.out.println("大吉大利,今晚吃鸡!");
}

信号量

Semaphore也是基于AQS实现,分为公平和非公平两种实现,Semaphore使用AQS共享模式(ReentrantLock是独占式),可以支持多个线程同时操作,多个线程共享同一把锁,但Semaphore限制了线程的数量,内部实现也是通过判断和改变state值实现的。

Semaphore可以用于流量控制,比如数据库链接,某一时刻,只允许固定个数的请求建立连接,避免数据库压力过大。

代码语言:javascript
复制
// 同时只有5个线程可以操作
private static final Semaphore semp = new Semaphore(5);
// 模拟30个请求
private static final int THREAD_COUNT = 30;
// 池
private static final ExecutorService pool = Executors.newCachedThreadPool();
public static void main(String[] args) {
    for (int i = 0; i < THREAD_COUNT; i++) {
        final int NO = i;
        Runnable run = () -> {
            try {
                semp.acquire();
                System.out.println("线程: " + NO + "执行……");
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            } finally {
                semp.release();
            }
        };
        pool.execute(run);
    }
    pool.shutdown();
}

可循环使用的屏障

CyclicBarrier内部使用ReentrantLock实现,它主要实现的是,让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障打开,所有被屏障拦截的线程才会继续运行。

CyclicBarrier与CountDownLatch相比,CountDownLatch只能使用一次,而CyclicBarrier可以通过reset方法重置。如:当发生异常时,重新开始,让线程重新跑一次,因此功能更灵活。

CyclicBarrier适用于一些需等待最后一个动作完成,再一起执行,如:百米比赛

代码语言:javascript
复制
// 百米比赛
private final static CyclicBarrier barrier = new CyclicBarrier(3, () -> {
    System.out.println("预备!");
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
    }
    System.out.println("发令枪响起!");
});
// 池
private final static ExecutorService pool = Executors.newFixedThreadPool(3);
public static void main(String[] args) {
    Athlete subingtian = new Athlete("苏炳添", 1, barrier);
    Athlete boerte = new Athlete("博尔特", 2, barrier);
    Athlete gaiyi = new Athlete("泰森盖伊", 3, barrier);
    pool.execute(subingtian);
    pool.execute(boerte);
    pool.execute(gaiyi);
    pool.shutdown();
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
    }
    System.out.println("苏炳添 冠军!!!");
}
代码语言:javascript
复制
class Athlete implements Runnable {
    private String name;
    private Integer time;
    private CyclicBarrier barrier;
    Athlete(String name, int time, CyclicBarrier barrier) {
        this.name = name;
        this.time = time;
        this.barrier = barrier;
    }
    @Override
    public void run() {
        System.out.println(name + "准备起跑!");
        try {
            barrier.await();
            TimeUnit.SECONDS.sleep(time);
        } catch (InterruptedException | BrokenBarrierException e) {
            System.out.println("异常");
        }
        System.out.println(name + "到达终点!");
    }
}
  1. 《Java核心技术 卷I》
  2. 《Java并发编程的艺术》
  3. http://www.ideabuffer.cn/2017/03/15/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3AbstractQueuedSynchronizer%EF%BC%88%E4%B8%80%EF%BC%89/
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-01-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 BanzClub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档