理解Java并发工具类Semaphore
Semaphore是Java里面另外一个基本的并发工具包类,主要的的作用是用来保护共享资源的访问的,也就是仅仅允许一定数量的线程访问共享资源。Semaphore维护了有限数量的许可证,只有得到了许可证的线程才能进行共享资源的访问,如果得不到许可证,说明当前共享资源的访问已经达到最大限制,所以会挂起当前线程,直到前面的线程处理完任务之后,把许可证归还,后面排队的线程才有机会获取,然后处理任务。
这里面有两个注意点:
(1)大多数时候使用Semaphore都应该是公平模式,默认是非公平模式,如果需要公平模式可以在构造函数里面指定,公平性可以 保证先进先出,不会有线程饥饿问题出现,非公平模式,不保证顺序,吞吐量会更好一些。
(2)共享资源的访问,一般指的是读取,而不是更新,这里面不要做对共享变量的修改,除非你使用同步块来保证。
下面我们来看下Semaphore的构造方法:
Semaphore(int permits) //非公平模式指定最大允许访问许可证数量
Semaphore(int permits, boolean fair)//可以通过第二个参数控制是否使用公平模式
一些常用的方法:
acquire() //申请获取一个许可证,如果没有许可证,就阻塞直到能够获取或者被打断
availablePermits() // 返回当前有多少个有用的许可证数量hasQueuedThreads()//查询是否有线程正在等待获取许可证
drainPermits()//获得并返回所有立即可用的许可证数量
getQueuedThreads()//返回一个List包含当前可能正在阻塞队列里面所有线程对象
getQueueLength()//返回当前可能在阻塞获取许可证线程的数量
hasQueuedThreads()//查询是否有线程正在等待获取许可证
isFair()//返回是否为公平模式
reducePermits(int reduction)//减少指定数量的许可证
reducePermits(int reduction)//释放一个许可证
release(int permits)//释放指定数量的许可证
tryAcquire()//非阻塞的获取一个许可证
无论是Semaphore还是CountDonwLatch或者是CyclicBarrier,其实我们都可以通过Lock接口+Condition条件队列功能来模拟实现,但是不够抽象所以才出现了AQS这个抽象的面向开发者同步框架,比如这个Semaphore,我们看下如果使用Lock实现:
public class SemaphoreDemo2 {
private final Lock lock=new ReentrantLock(true);
private final Condition condition=lock.newCondition();
private int permit;
public SemaphoreDemo2(int permit) {
this.permit=permit;
}
private void acquire(){
lock.lock();
try{
if(permit==0){
condition.await();//如果超过限制,就进入条件阻塞队列
}
System.out.println(Thread.currentThread().getName()+" 获得资源 .... ");
permit--;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private void release(){
lock.lock();
try{
permit++;
condition.signalAll(); //每当有一个释放令牌,就唤醒所有等待的线程
}finally {
lock.unlock();
}
}
}
下面我们看一下简单的使用例子:
Semaphore semaphore=new Semaphore(3);
Runnable runnable=new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 访问资源......");
semaphore.release();
}
};
for (int i = 0; i < 5; i++) {
Thread thread=new Thread(runnable);
thread.start();
}
Thread.sleep(3000);
输出结果:
Thread-0 访问资源......
Thread-1 访问资源......
Thread-2 访问资源......
Thread-4 访问资源......
Thread-3 访问资源......
注意上面的例子只有3个许可证,我们运行了5个线程,所以同时最多只能运行3个线程,另外两个会阻塞直到前面的线程归还了许可证。
Semaphore底层原理:
Semaphore底层与CountDownLatch类似都是通过AQS的共享锁机制来实现的,指定的数量会设置到AQS里面的state里面,然后对于每一个 调用acquire方法线程,state都会减去一,如果state等于0,那么调用该方法的线程会被添加到同步队列里面,同时使用 LockSupport.park方法挂起等待,知道有线程调用了release方法,会对state加1,然后唤醒共享队列里面的线程,注意这里如果是 公平模式,就直接唤醒下一个等待线程即可,如果是非公平模式就允许新加入的线程与已有的线程进行竞争,谁先得到就是谁的,如果新加入的 竞争失败,就会走公平模式进入队列排队。
总结:
本文主要介绍了并发工具包Semaphore其主要作用来限制对于共享资源的访问,接着我们又介绍了其特点,使用及注意事项,然后又给出了使用其他同步工具Lock+Condition实现的Semaphore,最后又简单介绍了其底层实现原理。写作不易,烦请大伙可以点击下广告,多多支持一下。
- 协议森林10 魔鬼细节 (TCP滑窗管理)
- CMD魔法堂:获取进程路径和PID值的方法集
- .Net魔法堂:提取注释生成API文档
- CMD魔法堂:CMD进入指定目录
- Apache IIS Tomcat共存,配置全能服务器
- 协议森林11 涅槃 (TCP重新发送)
- CentOS6.5菜鸟之旅:关于搜索的shell命令
- 协议森林12 天下为公 (TCP堵塞控制)
- .Net魔法堂:AssemblyInfo.cs文件详解
- 本地php环境不支持curl_exec的解决办法
- JAVA魔法堂:读取.properties配置文件
- Python深入04 闭包
- PHP获取时间戳的毫秒
- Python深入03 对象的属性
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- PHP使用mysqli同时执行多条sql查询语句的实例
- 在tensorflow下利用plt画论文中loss,acc等曲线图实例
- PHP生成短网址的思路以及实现方法的详解
- ThinkPHP 3.2.3实现加减乘除图片验证码
- Python数据相关系数矩阵和热力图轻松实现教程
- PHP中md5()函数的用法讲解
- thinkPHP框架实现的简单计算器示例
- Keras在训练期间可视化训练误差和测试误差实例
- Laravel如何自定义command命令浅析
- wordpress自定义标签云与随机获取标签的方法详解
- 使用Keras画神经网络准确性图教程
- PHP序列化的四种实现方法与横向对比
- PHP自动载入类文件函数__autoload的使用方法
- 安装python3.7编译器后如何正确安装opnecv的方法详解
- python数据类型强制转换实例详解