前言:在多线程环境的同步中,我们为了让每个线程具有同步的作用,经常采用synchronize、reetrantlock等同步手段进行上锁,以便在同一时间只能有一个线程具有访问变量和读写变量的权力。然而假如实际的业务场景是允许一组线程访问(组线程数量有限),如何控制一组线程的同步,如果再采取加锁的方法就有点过犹不及了。那么此时信号量就闪亮登场了,对于一组线程的同步访问,对它来说就是小菜一碟

本篇博客的目录

一:semphore的简介

二:semphore的使用方法

三:使用实例

四:总结

一:semphore的简介

1.1:概念

semphpore是jdk提供的一个并发工具类,它位于java.util.concurrent包下,在jdk中对它是这样定义的:

一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动,Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。简单解释一下这段概念:它就是说semphpore可以对一组线程进行限定,线程每次访问程序之前必须通过它的acquire()方法进入一个房间,也就是没有调用 acquire()方法,线程是无法读取程序的,然后再通过release()方法释放这个线程,其他线程后面才能进入,而它是否允许是通过计数来完成的。

1,.2:理解

举个通俗的例子,假如我们要从出发地A到目的地B,有一辆车,它只能容纳4个人,而我们一共有10个人要从A到B,这辆车就好比是资源,而人乘车这一行为就是线程访问资源,我们进入车需要车票,车票就可以理解为信号量,一次只能进入4个人,其他人只能等待(线程wait,处于阻塞状态),而到了目的地或者有人中途下车了,此时就是release(),释放信号量,那么等待的人才会获得允许上车,每次进入的时候,都会进行计数。

二:semphore的使用方法

2.1:acquire()方法

  acquire()
          从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断
  acquire(int permits)
          从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断

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:\Java\jdk 1.8.0_1\bin\java" -Didea.launcher.port=7532 "-Didea.launcher.bin.path=D:\IntelliJ IDEA 2016.3.4\bin" -Dfile.encoding=GBK -classpath "D:\Java\jdk 1.8.0_1\jre\lib\charsets.jar;D:\Java\jdk 1.8.0_1\jre\lib\deploy.jar;D:\Java\jdk 1.8.0_1\jre\lib\ext\access-bridge-32.jar;D:\Java\jdk 1.8.0_1\jre\lib\ext\cldrdata.jar;D:\Java\jdk 1.8.0_1\jre\lib\ext\dnsns.jar;D:\Java\jdk 1.8.0_1\jre\lib\ext\jaccess.jar;D:\Java\jdk 1.8.0_1\jre\lib\ext\jfxrt.jar;D:\Java\jdk 1.8.0_1\jre\lib\ext\localedata.jar;D:\Java\jdk 1.8.0_1\jre\lib\ext\nashorn.jar;D:\Java\jdk 1.8.0_1\jre\lib\ext\sunec.jar;D:\Java\jdk 1.8.0_1\jre\lib\ext\sunjce_provider.jar;D:\Java\jdk 1.8.0_1\jre\lib\ext\sunmscapi.jar;D:\Java\jdk 1.8.0_1\jre\lib\ext\sunpkcs11.jar;D:\Java\jdk 1.8.0_1\jre\lib\ext\zipfs.jar;D:\Java\jdk 1.8.0_1\jre\lib\javaws.jar;D:\Java\jdk 1.8.0_1\jre\lib\jce.jar;D:\Java\jdk 1.8.0_1\jre\lib\jfr.jar;D:\Java\jdk 1.8.0_1\jre\lib\jfxswt.jar;D:\Java\jdk 1.8.0_1\jre\lib\jsse.jar;D:\Java\jdk 1.8.0_1\jre\lib\management-agent.jar;D:\Java\jdk 1.8.0_1\jre\lib\plugin.jar;D:\Java\jdk 1.8.0_1\jre\lib\resources.jar;D:\Java\jdk 1.8.0_1\jre\lib\rt.jar;E:\Elas Search\Test\out\production\Test;D:\IntelliJ IDEA 2016.3.4\lib\idea_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提供给我们的便捷的开发工具,在并发中做到没有脏数据,高效、稳定是我们每个开发者都将追求的目标。好了,本期博客就写到这里。

Semphore信号量的使用的更多相关文章

  1. 与AQS有关的并发类

    ReetrantLock与Condition: 參考 在java.util.concurrent包中.有两个非常特殊的工具类.Condition和ReentrantLock,使用过的人都知道,Reen ...

  2. Python 多线程应用

    同步锁 import time import threading def subNum(): global num # print("ok") lock.acquire() # 加 ...

  3. Linux系统中的信号量(semphore)与互斥体(mutex)

    http://www.embexperts.com/viewthread.php?tid=31 两者最大区别:信号量可以允许多个线程进入临界区,而互斥体只允许一个线程进入临界区.本贴将描述信号量与互斥 ...

  4. 【C#】【Thread】Semaphore/SemaphoreSlim信号量

    System.Threading.Semaphore 类表示一个命名(系统范围)信号量或本地信号量. 它是一个对 Win32 信号量对象的精简包装. Win32 信号量是计数信号量,可用于控制对资源池 ...

  5. Linux进程间通信之信号量

    春节过去了,真的过去一年了.在公司待了快一年了.2016希望自己变得越来越好. ps:上面那句话是年前写的,中间隔了那么久,自己也变懒了. 一.信号量 1,信号量本质是一个计数器,控制访问共享资源的最 ...

  6. [转]Posix-- 互斥锁 条件变量 信号量

    这是一个关于Posix线程编程的专栏.作者在阐明概念的基础上,将向您详细讲述Posix线程库API.本文是第三篇将向您讲述线程同步. 互斥锁 尽管在Posix Thread中同样可以使用IPC的信号量 ...

  7. 进程间通信和同步:pipe、FIFO、消息队列、信号量、共享内存、信号

    一.半双工管道(pipe) 关于管道详细介绍可参考http://www.cnblogs.com/nufangrensheng/p/3560130.html. 1.管道实现父子进程间通信实例: /* p ...

  8. 线程间同步之 semaphore(信号量)

    原文地址:http://www.cnblogs.com/yuqilin/archive/2011/10/16/2214429.html semaphore 可用于进程间同步也可用于同一个进程间的线程同 ...

  9. [java多线程] - 锁机制&同步代码块&信号量

    在美眉图片下载demo中,我们可以看到多个线程在公用一些变量,这个时候难免会发生冲突.冲突并不可怕,可怕的是当多线程的情况下,你没法控制冲突.按照我的理解在java中实现同步的方式分为三种,分别是:同 ...

随机推荐

  1. TPO-14 C2 Prepare for a career in journalism

    TPO-14 C2 Prepare for a career in journalism 第 1 段 1.Listen to a conversation between a student and ...

  2. JavaScript 之 对象/JSON/数组

    对象 简单说,所谓对象,就是一种无序的数据集合,由若干个“键值对”(key-value)构成. var obj = { p: 'Hello World' }; 上面代码中,大括号就定义了一个对象,它被 ...

  3. Python+Flask+Gunicorn 项目实战(一) 从零开始,写一个Markdown解析器 —— 初体验

    (一)前言 在开始学习之前,你需要确保你对Python, JavaScript, HTML, Markdown语法有非常基础的了解.项目的源码你可以在 https://github.com/zhu-y ...

  4. vue移动音乐app开发学习(三):轮播图组件的开发

    本系列文章是为了记录学习中的知识点,便于后期自己观看.如果有需要的同学请登录慕课网,找到Vue 2.0 高级实战-开发移动端音乐WebApp进行观看,传送门. 完成后的页面状态以及项目结构如下: 一: ...

  5. java知乎爬虫

    好久没写博客了,前阵子项目忙着上线,现在有点空闲,就把最近写的一个爬虫和大家分享下,统计结果放在了自己买的阿里云服务器上(点此查看效果),效果如下: 程序是在工作之余写的,用了java 的webmgi ...

  6. php中array_map和array_walk的使用对比

    一.array_map() 1.array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组,若函数作用后无返回值,则对应的新值数组中为空. 2.回调 ...

  7. linux下php环境配置

    参: http://www.laozuo.org/5542.html LAMP,基于Linux/Apache/MySQL/PHP架构的网站建设环境,对于一般的网站来说足够使用,如果我们的网站访问量或者 ...

  8. ubuntu中下载sublime相关问题

    1.SublimeText3的安装 在网上搜索了一些ubuntu下关于sublime-text-3安装的方法,在这里针对自己尝试的情况进行反馈: 方法一(未成功): 在终端输入以下代码: sudo a ...

  9. C++并行编程1

    what is concurrency 我们可以一边看电视,一边唱歌.人并行非常容易理解,但是计算机呢?是不是我们一边编辑着word文档,一边听着歌,这样计算机就是在并行吗?不一定欧,如果你计算机是单 ...

  10. 语音信号处理之动态时间规整(DTW)(转)

    这学期有<语音信号处理>这门课,快考试了,所以也要了解了解相关的知识点.呵呵,平时没怎么听课,现在只能抱佛脚了.顺便也总结总结,好让自己的知识架构清晰点,也和大家分享下.下面总结的是第一个 ...