前言:在多线程环境的同步中,我们为了让每个线程具有同步的作用,经常采用synchronize、reetrantlock等同步手段进行上锁,以便在同一时间只能有一个线程具有访问变量和读写变量的权力。然而假如实际的业务场景是允许一组线程访问(组线程数量有限),如何控制一组线程的同步,如果再采取加锁的方法就有点过犹不及了。那么此时信号量就闪亮登场了,对于一组线程的同步访问,对它来说就是小菜一碟
本篇博客的目录
一:semphore的简介
二:semphore的使用方法
三:使用实例
四:总结
一:semphore的简介
1.1:概念
semphpore是jdk提供的一个并发工具类,它位于java.util.concurrent包下,在jdk中对它是这样定义的:
一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire()
,然后再获取该许可。每个 release()
添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore
只对可用许可的号码进行计数,并采取相应的行动,Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。简单解释一下这段概念:它就是说semphpore可以对一组线程进行限定,线程每次访问程序之前必须通过它的acquire()方法进入一个房间,也就是没有调用
acquire()方法,线程是无法读取程序的,然后再通过release()方法释放这个线程,其他线程后面才能进入,而它是否允许是通过计数来完成的。
1,.2:理解
二:semphore的使用方法
2.1:acquire()方法
|
acquire() 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。 |
|
acquire(int permits) 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。 |
2.2:release()方法
|
release() 释放一个许可,将其返回给信号量。 |
|
release(int permits)
释放给定数目的许可,将其返回到信号量。 |
release方法和acquire方法相对应,通acquire方法获取了通行证,那么当使用完资源的时候,出去的时候就要调用release方法进行释放锁,这样其他线程才有资格再调用acquire方法再次获取"通行证”。同样它提供了重载的方法可以允许一次释放很多线程。
三:使用实例
3.1:现在来模拟10个人乘车,一辆车只能容纳4个人,所以一次只能进入4个人,而其他人只能处于阻塞状态,只有获取许可,才能进入车中,当一个人下来的时候其他人才能进入继续乘车
3.2:程序实例
3.2.1:我们先来模拟一个人,定义它的行为乘车和下车,比较简单,下面给出示例代码:
public class Person { private static final String suffix="号开始乘车"; private static final String suffix2="号出来了"; /** * 编号 */ private Integer no; public Person(Integer no) { this.no = no; } public void riding(){ StringBuilder stringBuilder = new StringBuilder(); System.out.println(stringBuilder.append(this.getNo()).append(suffix)); } public void out(){ StringBuilder stringBuilder = new StringBuilder(); System.out.println(stringBuilder.append(this.getNo()).append(suffix2)); } public Integer getNo() { return no; } public void setNo(Integer no) { this.no = no; } }
3.2.2:再来定义一个乘车的线程,拥有信号量和人两个引用:
public class RideThread implements Runnable { private Semaphore semp; private Person person; public RideThread(Semaphore semp, Person person) { this.semp = semp; this.person = person; } public void run() { try { // 获取许可 semp.acquire(); person.riding(); Thread.sleep((long) (Math.random() * 10000)); person.out(); // 访问完后,释放 semp.release(); } catch (InterruptedException e) { e.printStackTrace(); } }; }
3.2.3:测试类
public class SemphoreTest { public static final Integer personNums=10; public static void main(String[] args) { // 线程池 ExecutorService exec = Executors.newCachedThreadPool(); // 只能4个人能同时上车 final Semaphore semp = new Semaphore(4); // 模拟10个人乘车 for (int index = 0; index < personNums; index++) { final int NO = index; Person person = new Person(NO); exec.submit(new RideThread(semp,person)); } System.out.println(semp.isFair()); // 退出线程池 exec.shutdown(); } }
3.3:输出结果
"D:Javajdk 1.8.0_1injava" -Didea.launcher.port=7532 "-Didea.launcher.bin.path=D:IntelliJ IDEA 2016.3.4in" -Dfile.encoding=GBK -classpath "D:Javajdk 1.8.0_1jrelibcharsets.jar;D:Javajdk 1.8.0_1jrelibdeploy.jar;D:Javajdk 1.8.0_1jrelibextaccess-bridge-32.jar;D:Javajdk 1.8.0_1jrelibextcldrdata.jar;D:Javajdk 1.8.0_1jrelibextdnsns.jar;D:Javajdk 1.8.0_1jrelibextjaccess.jar;D:Javajdk 1.8.0_1jrelibextjfxrt.jar;D:Javajdk 1.8.0_1jrelibextlocaledata.jar;D:Javajdk 1.8.0_1jrelibext ashorn.jar;D:Javajdk 1.8.0_1jrelibextsunec.jar;D:Javajdk 1.8.0_1jrelibextsunjce_provider.jar;D:Javajdk 1.8.0_1jrelibextsunmscapi.jar;D:Javajdk 1.8.0_1jrelibextsunpkcs11.jar;D:Javajdk 1.8.0_1jrelibextzipfs.jar;D:Javajdk 1.8.0_1jrelibjavaws.jar;D:Javajdk 1.8.0_1jrelibjce.jar;D:Javajdk 1.8.0_1jrelibjfr.jar;D:Javajdk 1.8.0_1jrelibjfxswt.jar;D:Javajdk 1.8.0_1jrelibjsse.jar;D:Javajdk 1.8.0_1jrelibmanagement-agent.jar;D:Javajdk 1.8.0_1jrelibplugin.jar;D:Javajdk 1.8.0_1jrelib esources.jar;D:Javajdk 1.8.0_1jrelib t.jar;E:Elas SearchTestoutproductionTest;D:IntelliJ IDEA 2016.3.4libidea_rt.jar" com.intellij.rt.execution.application.AppMain Semphore.SemphoreTest 1号开始乘车 0号开始乘车 3号开始乘车 2号开始乘车 false 3号出来了 4号开始乘车 2号出来了 5号开始乘车 4号出来了 6号开始乘车 5号出来了 7号开始乘车 1号出来了 8号开始乘车 8号出来了 9号开始乘车 7号出来了 0号出来了 6号出来了 9号出来了
可以看出来最开始先进入4个人乘车,首先4个人获取了许可证,然后后面的都是一个出来,另一个进去,只有当一个线程获取信号量再释放信号量的时候,其它线程才能乘车。这样按照顺序,严格限定每次只有空位的时候其他线程才能访问资源!观察结果,会发现最开始的4个是杂序的(后面进入的顺序是for循环控制的),这是“非公平的”,因为在构造Semphore的时候,没有限定第二个参数isFair,这样默认是非公平的,符合按照顺序来进行线程的访问,假如要公平的话,我们可以指定第二个参数为true,这样构造出来的Semhore就是公平的,很多线程去抢,谁抢到是谁的,我们把第二个参数设置为true来观察一下输出的结果:
是否是公平锁:true 0号开始乘车 1号开始乘车 2号开始乘车 3号开始乘车
执行了很多次,都可以看出来最开始的线程是按照顺序进行的,这就是公平信号量,严格遵守顺序依次执行!
四:总结
本篇博客讲述了Semphore的使用方法,只是抛砖引玉简单的阐述了几个重要的方法和基本使用,在实际的开发中,会遇到更加复杂的业务场景,如何选择jdk提供给我们的便捷的开发工具,在并发中做到没有脏数据,高效、稳定是我们每个开发者都将追求的目标。好了,本期博客就写到这里。