您当前的位置: 首页 > 

OMG!Semaphore里面居然有这么一个大坑!

CSDN 程序人生 发布时间:2020-08-05 14:02:36 ,浏览量:3

作者 | why技术

来源 | why技术(ID:hello_hi_why)

荒腔走板

上周写了一篇文章,一不小心戳到了大家的爽点,其中一个转载我文章的大号,阅读量居然突破了 10w+,我也是受宠若惊。

但是其实我是一个技术博主来的,偶尔写点生活相关的。所以这篇还是回到技术上。

但是我的技术文章有个特点是第一张图片都是我自己拍的。然后我会围绕这个图片进行一个简短的描述,我称之为荒腔走板环节。

目的是给冰冷的技术文注入一丝色彩。

我这样做已经坚持了很多篇,有的读者给我说:看完荒腔走板部分就退出去了。

那你们是真的棒哦,至少退出去之前,拉到文末,来个一键三连吧,给我来点正反馈。

好了,先说说这期的荒腔走板。

上面这个图片是我上周末看《乐队的夏天》的时候拍的。

这个乐队的名字叫做水木年华,我喜欢这个乐队。

我听他们的歌的时候,应该是初中,那个时候磁带已经差不多快过气了,进入了光碟的时代,我记得一张光碟里面有好几十首歌,第一次在 DVD 里面听到他们的歌是《一生有你》,听到这首歌的时候就感觉很干净,很惊艳。

然后一字一句抄在自己的歌词本上。

听到这首歌的那个周末,我就看着那个 MV 反复学,那时的 DVD 有个功能是可以 A-B 反复播放某个片段,我就一句一句的学,学会了这首歌。

那时候的李健,一双清澈明亮的大眼睛,就像一汪湖水,我一个小男孩,都好想在他的眼睛里扎个猛子。

这首歌,我愿称之为校园民谣的巅峰之一。

十多年后的今天,这个乐队重新出现在我的视野中,只是李健已经不再其中。

他们在乐队的夏天的舞台上唱了一首《青春再见》,结果被一个自称 23 岁的胖小伙说“中年人的油腻”,被另个专业乐迷说:“四十多岁的人怎么还在唱青春再见?”。第一期就被淘汰出局。

这操作,看的我一愣一愣的。

这个怎么就油腻了?四十多岁的人怎么就不能唱青春再见了?男人至死都是少年你们不知道吗?小子,他们玩音乐的时候你还不会说话呢。

他们离开舞台的画面,我感觉到一丝辛酸,一丝真的青春再见的辛酸。

水木年华没有错,错的是这个舞台,这个舞台不适合他们的歌曲。

好了,说回文章。

一起看个问题

前几天有个读者给我发了一个链接,说这个链接里面的代码,为什么会这样运行,实在是没有搞懂是怎么回事,链接如下:

https://springboot.io/t/topic/1139

代码是这样的,给大家上个图:

注意第 10 行,permits 参数,根据他的描述应该是 3:

不知道为什么代码里面给了一个 2。但是为了保证真实,我直接拿过来了,没有进行改动。一会我会根据这个代码进行简单的修改。

知道 semaphore 是干啥的同学可以先看看上面的代码,为什么造成了“死锁”。

反正是一个非常无语的低级错误,但是我反复看了几遍居然没有看出来。

不知道 semaphore 是干啥的同学,看过来。我先给你科普一下。

semaphore 我们一般叫它信号量,用来控制同时访问指定资源的线程数量。

如果不懂 semaphore ,那上面代码你也看不懂了,我按照代码的逻辑给你举个例子。

比如一个高端停车场,只有 3 个车位。(这就是“指定资源”)

现在里面没有停车,那么它最多可以停几辆车呢?

是的,门口的剩余车辆指示牌显示:剩余停车位 3 辆。

这个时候,有三路人想要过来停车。

三条路分别是:转发路、点赞路、赞赏路。

路上的车分别是 why 哥的劳斯莱斯、赵四的布加迪、刘能、谢广坤这对好基友开的法拉利:

这个时候从“点赞路”过来的赵四先开到了,于是停了进去。

门口的停车位显示:剩余停车位 2 辆。

刘能、谢广坤到了后发现,刚好还剩下 2 个车位,于是好基友手拉手,一起停了进去。

门口的停车位显示:余下车位 0 辆。

没多久,我也到了,发现没有停车位了,怎么办呢?我只有在门口等一下了。

没一会,赵四办完事了,开着他的布加迪走了。

门口的停车位显示:余下车位 1 辆。

我赶紧停进去。

门口的停车位显示:余下车位 0 辆。

上面的代码想要描述的就是这样的一个事情。

但是根据提问者的描述,“在运行时,有时只会执行完线程A,其线程B和线程C都静默了。”

在上面这个场景中就是:赵四的布加迪开进去停车后,后面刘能、谢广坤的法拉利和我的劳斯莱斯都停不进去了。

就是这样式儿的:

为什么停不进去呢?他怀疑是死锁了,这个怀疑有点无厘头啊。

我们先回忆一下死锁的四个必要条件:

  • 互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。(不满足,还有两个停车位没有用呢。)

  • 请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。(不满足,张三占了一个停车位了,没有提出还要一个停车位的要求,另外的停车位也没有被占用)

  • 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放。(满足,张三的车不开出来,这个停车位理论上是不会被夺走的)

  • 循环等待条件: 若干进程间形成首尾相接循环等待资源的关系。(不满足,只有我和刘能、谢广坤两拨人在等资源,但没有循环等待的情况。)

这四个条件是死锁的必要条件,必要条件就是说只要有死锁了,这些条件必然全部成立。

而经过分析,我们发现没有满足死锁的必要条件。那为什么会出现这样的现象呢?

我们先根据上面的场景,自己写一段代码。

自己撸代码

下面的程序基本上是按照上面截图中的示例代码结合上面的故事改的,可以直接复制粘贴:

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

        Integer parkSpace = 3;
        System.out.println("这里有" + parkSpace + "个停车位,先到先得啊!");
        Semaphore semaphore = new Semaphore(parkSpace, true);

        Thread threadA = new Thread(new ParkCar(1, "布加迪", semaphore), "赵四");
        Thread threadB = new Thread(new ParkCar(2, "法拉利", semaphore), "刘能、谢广坤");
        Thread threadC = new Thread(new ParkCar(1, "劳斯莱斯", semaphore), "why哥");

        threadA.start();
        threadB.start();
        threadC.start();
    }
}

class ParkCar implements Runnable {
    
    private int n;
    private String carName;
    private Semaphore semaphore;

    public ParkCar(int n, String carName, Semaphore semaphore) {
        this.n = n;
        this.carName = carName;
        this.semaphore = semaphore;
    }

    @Override
    public void run() {
        try {
            if (semaphore.availablePermits()             
关注
打赏
1688896170
查看更多评论
0.0572s