多线程同步【5】之Semaphore

老子说过,“天下大事必做于细,天下难事必做于易”。人要想有所作为,首先得从细微之处入手,从简单的事情做起。中国前国家队足球教练米卢也曾经说过:“态度决定一切。”,“态度决定一切,细节决定成败”确实是至理名言,在生活中、工作中,学习中,为人处事,都应该端正态度,注重细节,从小事做起,从身边做起。我们只有树立正确的态度,做好了细节,并且坚持下来,才容易成功。

继续总结多线程同步常用的方法或者类,之前介绍了CountDownLatch,CyclicBarriar和Exchanger和Phaser,这次介绍一个比较有名的类--Semaphore,大多数人看到它都不会陌生,但是要问怎么正确的使用它,很多人却回答不上来。

Semaphore--信号量
Semaphore的一个特别典型的应用场景是:线程执行是需要耗费CPU的时间片的,并且占用CPU的资源,例如上下文切换,如果不对线程数量进行控制,CPU工作时同时并发的线程数量可能会非常多,这样就会导致每个线程运行起来非常的缓慢,严重影响到系统的执行效率,它的工作效率将大幅下降。因此,此时我们就可以用信号量来进行控制,从而让CPU能够发挥出最大的效率。

1、定义

Semaphore中文翻译为信号量,它用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用公共资源。在创建Semaphore时会设置令牌,这里的令牌就可以看做允许访问特定资源的线程数量,通过设置它来控制线程数量,这个令牌可能是1,也可能是大于1的任意数字。哪个线程拿到(acquire)了令牌,它就可以去执行了,如果没有令牌则需要等待。 线程执行完毕,一定要归还(release)令牌,否则令牌会被用光,别的线程就无法获得令牌而只能永久等待。

可以这样形象的来理解,
Semaphore对应的功能就类似于公司只有一个厕所,而厕所里仅仅有3个茅坑,如果这时候有10个人都要想上厕所,那么显而易见同时只能有3个人能够占用这些茅坑,其他7个人只能在厕所门口等待,当有人从厕所出来后,才能再进入一个人。

2、常用方法

Semaphore的方法比较多, 我们只介绍一些最常用的方法,有兴趣想深入学习的可以去找相关资料再深入学习。

● Semaphore(int permits)
新建一个Semaphore对象,参数permits是初始化设置许可的数量,即令牌的数量。它表示最多允许permits个线程执行acquire()与release()之间的代码。

● Semaphore(int permits, boolean fair)
新建一个Semaphore对象,参数permits是初始化设置许可的数量,即令牌的数量。参数fair表明创建的Semaphore对象是公平信号量(fair为true)还是非公平信号量(fair为false)。所谓公平信号量表明获得锁的顺序与线程启动的顺序有关,启动早的线程优先获得令牌,但并不代表100%的能够获得信号量,仅仅是在概率上能够优先获得。而非公平信号量就是和公平无关的,线程无论谁先启动,这个参数都无效。

● acquire(int permits)
每调用一次该方法,则消耗permits个许可。

● acquire()
每调用一次该方法,则消耗一个许可。

● release()
每调用一次该方法,相当于许可加1。

● release(int permits)
每调用一次该方法,相当于加上permits个许可。在实际开发中,acquire()与release()成对出现,以保证每个线程用时消耗,用完释放。

● acquireUninterruptibly()
使得等待进入acquile()方法的线程不允许被中断。

● acquireUninterruptibly(int permits)
使得等待进入acquile()方法的线程不允许被中断,且获得锁后,将消耗permits个许可。

● availablePermits()
返回此Semaphore对象当前可用的许可数,此方法通常用于调试,因为许可的数量有可能在实时改变,并不是固定的数量。

● drainPermits()
返回此Semaphore对象当前可用的许可数,并将可用许可清零。

3、演示代码

public class TestSemaphore {
   public static void main(String[] args) {

      // 线程池
      ExecutorService exec = Executors.newCachedThreadPool();

      // 只能3个茅坑同时使用
      final Semaphore semp = new Semaphore(3);

      // 模拟10个人同时访问厕所
      for (int index = 0; index < 10; index++) {
         final int NO = index;

         Runnable run = new Runnable() {
            public void run() {
               try {
                  // 获取空余的茅坑
                  semp.acquire();
                  System.out.println("get a noun: " + NO);
                  Thread.sleep((long) (Math.random() * 10000));

                  //使用完后,释放茅坑
                  semp.release();
                  System.out.println("release,availablePermits : "+semp.availablePermits());
               } catch (InterruptedException e) {
                  e.printStackTrace();
               }
            }
         };
         exec.execute(run);
      }
      // 线程池退出
      exec.shutdown();
   }
}

执行结果如下:

get a noun: 0

get a noun: 2

get a noun: 1

release,availablePermits : 0

get a noun: 9

release,availablePermits : 1

get a noun: 8

release,availablePermits : 1

get a noun: 6

release,availablePermits : 1

get a noun: 4

release,availablePermits : 1

get a noun: 3

release,availablePermits : 1

get a noun: 5

release,availablePermits : 1

get a noun: 7

release,availablePermits : 1

4、总结

说实话,本司机开发了十几年程序了,Semaphore使用的情况还真的是很少,可以说几乎没有,但是这也不妨碍我们学习它,对它进行了解,说不定哪天就遇到了使用它的场景,到时我们能够立即想到用它来解决就OK了。另一方面学习它也扩展了我们的知识面,不至于在别人谈论时,我们还不知道它是一个什么东东。

多线程同步【5】之Semaphore_第1张图片
本公众号将以推送Android各种技术干货或碎片化知识,以及整理老司机日常工作中踩过的坑涉及到的经验知识为主,也会不定期将正在学习使用的新技术总结出来进行分享。每天一点干货小知识把你的碎片时间充分利用起来。

你可能感兴趣的:(多线程同步【5】之Semaphore)