前言:在多线程环境的同步中,我们为了让每个线程具有同步的作用,经常采用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. OSG-OSG中的observer_ptr指针

    看array大神的CookBook后一些感想,在代码上添加了一些注释,也对源码做了一些研读,记录下学习的过程. CookBook中第一个例子就是observer_ptr指针,这个指针和它的名字一样,就 ...

  2. Python3安装pywin32模块

    假如你安装的是Python3.6, 那么可以直接用PyCharm或者pip安装pywin32模块: 但是, 由于我安装的是Python3.7, 所以PyCharm或者pip都无法成功安装pywin32 ...

  3. mysql新手进阶03

    当年忠贞为国酬,何曾怕断头? 如今天下红遍,江山靠谁守? 业未就,身躯倦,鬓已秋. 你我之辈,忍将夙愿,付与东流? 数据库结构如下: 仓库(仓库号, 城市, 面积) 订购单(职工号, 供应商号, 订购 ...

  4. Java Web开发框架Spring+Hibernate整合效果介绍(附源码)(已过期,有更好的)

    最近花了一些时间整合了一个SpringMVC+springAOP+spring security+Hibernate的一套框架,之前只专注于.NET的软件架构设计,并没有接触过Java EE,好在有经 ...

  5. android AVD创建

    参数详解:AVD name:是要填写的虚拟机名称,这个自己随便取名就行了,要纯英文和数字组成Device:这里是要选择模拟的设备,一般选择3.2*QVGA(ADP2)(320*480: mdpi)这个 ...

  6. 棋盘问题:dfs

    Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子 ...

  7. Trie 树——搜索关键词提示

    当你在搜索引擎中输入想要搜索的一部分内容时,搜索引擎就会自动弹出下拉框,里面是各种关键词提示,这个功能是怎么实现的呢?其实底层最基本的就是 Trie 树这种数据结构. 1. 什么是 "Tri ...

  8. Hadoop源码解析 1 --- Hadoop工程包架构解析

    1 Hadoop中各工程包依赖简述     Google的核心竞争技术是它的计算平台.Google的大牛们用了下面5篇文章,介绍了它们的计算设施.     GoogleCluster: http:// ...

  9. Python学习 - 入门篇1

    前言 学习渠道:慕课网:Python入门 记录原因:人总归要向记忆低头[微笑再见.gif] 记录目标:形成简洁的知识点查阅手册 变量和数据类型 变量 赋值 在Python中,可以把任意数据类型赋值给变 ...

  10. android 出现Make sure the Cursor is initialized correctly before accessing data from it

    Make sure the Cursor is initialized correctly before accessing data from it 详细错误是:java.lang.IllegalS ...