《蹲坑也能进大厂》多线程系列 - 线程同步神器七星刀之Sem

前言

多线程系列我们前面已经更新过多个章节,强烈建议小伙伴按照顺序学习

《蹲坑也能进大厂》多线程系列文章目录

Semaphore称为信号量,是JUC中常见的一种工具类,该类使用起来比较简单,唯一的难点就是很多小伙伴们,对于信号量是什么很难理解,今天花Gie就来简单扼要的分析一波。

一、信号量是什么

  • 生活场景举例

我国有很多污染比较严重的工厂,但是为了经济发展,又不能对其全部关停,因此国家就有一个折中的办法,每个工厂需要向环境保护行政主管部门申请排污许可证。只要拥有许可证的工厂才允许合法运作。但是每年的排污许可证又是有数量限制,不能允许过多的工厂申请,否则就无法起到环境污染程度的控制的。

  • Java中信号量

在Java中,Semaphore就是上面的排污许可证,也就是信号量。那我们有哪些场景会用到信号量呢,比如我们调用某个方法,而该方法内部是下载文件或大数据量处理操作,所以非常耗时,这个时候如果我们不对该方法加以限制,当一次性过多线程调用时,可能会拖垮整个服务,Semaphore在这里就起到限制访问线程数量的作用。

image.png

二、Semaphore核心方法

Semaphore和CountDownLatch一样,内部维护一个核心属性sync,通过AQS的共享锁机制实现,这个后续会AQS会详细介绍

1
arduino复制代码private final Sync sync;

看一下Semaphore的几个核心方法:

  • Semaphore(int premits,boolean fair): 构造器方法。permits为信号量初始化数量,第二个参数fair可以设置是否需要公平策略,如果传入true,那么Semaphore会把等待的线程放入FIFO队列中,以便许可证被释放后,可以分配给等待时间最长的线程;
  • acquire(): 试图获取许可证,如果当前没有可用的,就会进入阻塞等待状态;
  • tryAcquire(): 试图获取许可证,如果是否能够获取,都不会进入阻塞。
  • tryAcquire(long timeout, TimeUnit unit): 和tryAcquire一样,只是多了一个超时时间,等待指定时间还获取不到许可证,就会停止等待;
  • availablePermits: 获取可用许可证数量;
  • release(): 释放一个许可证;
  • release(int permits): 释放指定数量的许可证。

三、代码演示

现在创建100个线程,每个线程每次只获取(acquire(1))一个信号量,并将semaphore数量初始化为3,,我们先看下代码,试想一下结果打印的会是什么呢。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
csharp复制代码public class SemaphoreDemo {
   static Semaphore semaphore = new Semaphore(3,true);
   public static void main(String[] args) {
       ExecutorService service = Executors.newFixedThreadPool(5);
       for (int i = 0; i < 100; i++) {
           service.submit(new Runnable() {
               @Override
               public void run() {
                   try {
                       semaphore.acquire(1);
                  } catch (InterruptedException e) {
                       e.printStackTrace();
                  }
                   System.out.println(Thread.currentThread().getName()+"成功获取许可证");
                   try {
                       Thread.sleep(1000);
                  } catch (InterruptedException e) {
                       e.printStackTrace();
                  }
                   System.out.println(Thread.currentThread().getName()+"释放许可证");
                   semaphore.release();
              }
          });
      }
       service.shutdown();
       semaphore.tryAcquire();
  }
}

打印结果如下:

image.png

可以清楚的看到,每次会有三个线程获取到信号量,只有信号量被释放后,其他线程才能继续获取。

代码过程图示如下:

  • 初始化semaphore许可证数量为3;
  • 线程1、线程2、线程3可以正常获取许可证,并方位服务;
  • 线程4访问时由于许可证数量不足,进入阻塞;
  • 当线程1释放许可证后,线程4使用acqire获取,正常访问服务。

image.png

上面我们演示了:使用semaphore.acquire()来获取信号量,这时每个线程只会拿到一个信号量,但如果我们将代码改成acquire(2)呢,这样会出现什么情况,这个时候会有几个线程能够同时执行呢,接下来继续代码演示一下。

1
2
3
scss复制代码//只需要修改两处代码
semaphore.acquire(2);
semaphore.release(1);

结果如下:

image.png

其实这个是很容易理解的,因为一个线程一次获取两个许可证,但是只释放一个,所以许可证两轮之后不足以被其他线程再次获取,其他线程就会被阻塞。

四、注意事项

  • 获取释放的许可证数量必须一致,否则随着时间的推移,最后许可证数量不够用,会导致线程卡死。
  • Semaphore设置是否公平性时,一般设置为true比较合理,因为Semaphore使用场景就是用在耗时较长的操作,如果被反复插队,线程就会持续陷入等待。
  • 获取释放的许可证不要求为同一个线程,只要满足我们业务需要,可以由A线程获取许可证,让B线程来释放。

总结

以上就是关于信号量的全部内容,总体看来,用法比较简单,再结合实际场景中许可证的栗子,我们掌握Semaphore会容易很多,今天又是收获满满的一天,下一章说些什么呢,花哥会和大家分享另外一个有趣的知识点-Condition,我们下期见咯。

点关注,防走丢

以上就是本期全部内容,如有纰漏之处,请留言指教,非常感谢。我是花Gie ,有问题大家随时留言讨论 ,我们下期见🦮。

文章持续更新,可以微信搜一搜 【花哥编程】 第一时间阅读,并且可以获取面试资料学习视频等,有兴趣的小伙伴欢迎关注,一起学习,一起哈🐮🥃。

原创不易,你怎忍心白嫖,如果你觉得这篇文章对你有点用的话,感谢老铁为本文点个赞、评论或转发一下,因为这将是我输出更多优质文章的动力,感谢!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%