java 中Semaphere可类比操作系统信号量,硬件资源如IO、内存、磁盘等都是有固定量的,多个程序需要竞争这些资源,没有资源就需要被挂起。

一、类和方法摘要

构造函数:

public Semaphore(int permits):创建具有给定的许可数和非公平的公平设置的 Semaphore。 
-参数 : 
permits - 初始的可用许可数目。此值可能为负数,在这种情况下,必须在授予任何获取前进行释放。

public Semaphore(int permits, boolean fair):创建具有给定的许可数和给定的公平设置的 Semaphore。 
-参数: 
permits - 初始的可用许可数目。此值可能为负数,在这种情况下,必须在授予任何获取前进行释放。 
fair - 如果此信号量保证在争用时按先进先出的顺序授予许可,则为 true;否则为 false。

常用方法:

void acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。

void release():释放一个许可,将其返回给信号量。

int availablePermits():返回此信号量中当前可用的许可数。

boolean hasQueuedThreads():查询是否有线程正在等待获取。

二、使用技巧

将信号量初始化为 1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁。这通常也称为二进制信号量,因为它只能有两种状态:一个可用的许可,或零个可用的许可。按此方式使用时,二进制信号量具有某种属性(与很多 Lock 实现不同),即可以由线程释放“锁”,而不是由所有者(因为信号量没有所有权的概念)。在某些专门的上下文(如死锁恢复)中这会很有用。

三、对比线程池

线程池控制的是线程数量,而信号量控制的是并发数量,虽然说看起来一样,但两者还是有区别的。

信号量类似于锁机制,信号量的调用,当达到数量后,线程还是存在的,只是被挂起了而已。而线程池,同时执行的线程数量是固定的,超过了数量的只能等待。

代码示例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore; public class SemaphoreTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3);
for(int i=0;i<10;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
sp.acquire();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() +
"进入,当前已有" + (3-sp.availablePermits()) + "个并发");
try {
Thread.sleep((long)(Math.random()*10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() +
"即将离开");
sp.release();
//下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
System.out.println("线程" + Thread.currentThread().getName() +
"已离开,当前已有" + (3-sp.availablePermits()) + "个并发");
}
};
service.execute(runnable);
}
} }

输出:

线程pool-1-thread-1进入,当前已有3个并发
线程pool-1-thread-3进入,当前已有3个并发
线程pool-1-thread-2进入,当前已有3个并发
线程pool-1-thread-1即将离开
线程pool-1-thread-1已离开,当前已有2个并发
线程pool-1-thread-4进入,当前已有3个并发
线程pool-1-thread-3即将离开
线程pool-1-thread-3已离开,当前已有2个并发
线程pool-1-thread-5进入,当前已有3个并发
线程pool-1-thread-4即将离开
线程pool-1-thread-4已离开,当前已有2个并发
线程pool-1-thread-6进入,当前已有3个并发
线程pool-1-thread-2即将离开
线程pool-1-thread-2已离开,当前已有2个并发
线程pool-1-thread-7进入,当前已有3个并发
线程pool-1-thread-7即将离开
线程pool-1-thread-7已离开,当前已有2个并发
线程pool-1-thread-8进入,当前已有3个并发
线程pool-1-thread-5即将离开
线程pool-1-thread-5已离开,当前已有3个并发
线程pool-1-thread-9进入,当前已有3个并发
线程pool-1-thread-6即将离开
线程pool-1-thread-6已离开,当前已有2个并发
线程pool-1-thread-10进入,当前已有3个并发
线程pool-1-thread-8即将离开
线程pool-1-thread-8已离开,当前已有2个并发
线程pool-1-thread-10即将离开
线程pool-1-thread-10已离开,当前已有1个并发
线程pool-1-thread-9即将离开
线程pool-1-thread-9已离开,当前已有0个并发

可以同时有3个线程执行sp.acquire()到sp.release()之间的代码。

出处:https://www.cnblogs.com/pony1223/p/9303009.html

JAVA多线程学习十二 - Semaphere同步工具的更多相关文章

  1. “全栈2019”Java多线程第二十二章:饥饿线程(Starvation)详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. “全栈2019”Java多线程第十八章:同步代码块双重判断详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  3. “全栈2019”Java多线程第十二章:后台线程setDaemon()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  4. Java多线程学习(二)synchronized关键字(2)

    转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79670775 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...

  5. Java多线程学习(二)synchronized关键字(1)

    转载请备注地址: https://blog.csdn.net/qq_34337272/article/details/79655194 Java多线程学习(二)将分为两篇文章介绍synchronize ...

  6. JAVA多线程学习十六 - 同步集合类的应用

    1.引言 在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的.在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操 ...

  7. Java多线程学习(二)

    一.定义产生返回值的任务 在上一篇文的介绍中,我们知道了定义任务通常的方法是定义一个实现Runnable接口的类,这个类被我们成为任务.然而也很容易注意到,任务的最重要的一个方法就是run( )方法, ...

  8. java web 学习十二(session)

    一.Session简单介绍 在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下).因此,在需要保存用户数据时,服务 ...

  9. JAVA多线程提高十二:阻塞队列应用

    一.类相关属性 接口BlockingQueue<E>定义: public interface BlockingQueue<E> extends Queue<E> { ...

随机推荐

  1. cosface: large margin cosine loss for deep face recognition

    目录 概 主要内容 Wang H, Wang Y, Zhou Z, et al. CosFace: Large Margin Cosine Loss for Deep Face Recognition ...

  2. 求区间内第一个大于等于x的数的下标

    int tree[4*N]; void build(int o,int l,int r) { if(l==r) {cin>>tree[o];return;} build(ls,l,mid) ...

  3. Gradle sync failed (3 s 402 ms) - Android Studio问题解决方法

    问题:Gradle同步失败 解决方法: 删除gradle-wrpper.jar中的 implementation 'com.android.support:design:30.+' 最后在Build中 ...

  4. JS中void(0)操作符的使用

    今天 在看源码时,发现这种写法 if(value === void(0)){ // } 以前没有见过这种写法,感觉就是判断一个变量是否有值,官网上是这样说的: void运算符 对给定的表达式进行求值, ...

  5. 更加高效的遍历 Map

    https://stackoverflow.com/questions/46898/how-do-i-efficiently-iterate-over-each-entry-in-a-java-map ...

  6. 官方文档粗读 - Tutorial

    参考: https://www.jianshu.com/p/0d234e14b5d3 1.Connecting 我们通过 create_engine() 来链接数据库,假设我们我们采用SQLite. ...

  7. 【PowerShell】格式化输出字符串

    1 '{0:d4}' -f 10 2 '数字的补零';{} 3 '{0:f4}' -f 10 4 '保留小数位数';{} 5 '{0:p2}' -f 0.4567 6 '转换为百分比';{} 7 '{ ...

  8. Java 单引号 与 双引号 区别

    双引号,用来引用字符串, 单引号用来表示单个字符.

  9. Java日期格式化带来的年份不正确

    BUG现场 一个线上项目之前一直运行得很稳定,从没出过数据错误的问题,但是在2021.12.26这天却"意外"地出现了数据计算错误. 刚开始一头雾水,不知道是什么问题,后来经过日志 ...

  10. 记一次 .NET 某消防物联网 后台服务 内存泄漏分析

    一:背景 1. 讲故事 去年十月份有位朋友从微信找到我,说他的程序内存要炸掉了...截图如下: 时间有点久,图片都被清理了,不过有点讽刺的是,自己的程序本身就是做监控的,结果自己出了问题,太尴尬了 二 ...