本博客系列是学习并发编程过程中的记录总结。由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅。

并发编程系列博客传送门


Semaphore([' seməf :(r)])的主要作用是控制线程并发的数量。我们可以将Semaphore想象成景区的一个门卫,这个门卫负责发放景区入园的许可证。

景区为了游客的入园观赏体验,决定最多允许200个有个同时在园内观赏。那么这个门卫在每天开园的时候手中都会有200张许可证,每当一个游客要入园的时候门卫会给游客发放一张许可证,当门卫手中的许可证发完之后再有游客需要入园的话就必须等待。

当游客观赏完毕之后,出园的时候需要将许可证交还到门卫手上。门卫将这些交还的许可证再发等待的游客,这些游客就能顺利入园了。

Semaphore的API简介

Semaphore的API使用起来也比较简单,常见的API简介如下:

  • Semaphore(int permits):构造方法,创建具有给定许可数的计数信号量并设置为非公平信号量。
  • Semaphore(int permits,boolean fair):构造方法,当fair等于true时,创建具有给定许可数的计数信号量并设置为公平信号量。
  • void acquire():从此信号量获取一个许可前线程将一直阻塞。相当于一辆车占了一个车位。
  • void acquire(int n):从此信号量获取给定数目许可,在提供这些许可前一直将线程阻塞。比如n=2,就相当于一辆车占了两个车位。
  • boolean tryAcquire(int permits, long timeout, TimeUnit unit):尝试获取,在给定的时间内没获取到资源超时
  • void release():释放一个许可,将其返回给信号量。就如同车开走返回一个车位。
  • void release(int n):释放n个许可。
  • int availablePermits():当前可用的许可数。

Semaphore的常见用法

下面给出一个Oracle官方文档中的列子代码:

class Pool {
// 可同时访问资源的最大线程数
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
// 共享资源
protected Object[] items = new Object[MAX_AVAILABLE];
protected boolean[] used = new boolean[MAX_AVAILABLE];
public Object getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x))
available.release();
}
private synchronized Object getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null;
}
private synchronized boolean markAsUnused(Object item) {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
} else
return false;
}
}
return false;
}
}

items数组可以看成是我们的共享资源,当有线程尝试使用共享资源时,我们要求线程先获得“许可”(调用Semaphoreacquire方法),这样线程就拥有了权限,否则就需要等待。当使用完资源后,线程需要调用Semaphorerelease方法释放许可。

Semaphore并不能替代synchronized

有些书中提到:如果将Semaphore的许可证数量设置成1的话,就能实现synchronized的功能。其实这种说法是不对的。

下面使用Semaphore来控制对一个账户进行并发存钱和取钱的动作,如果Semaphore能实现synchronized的功能的话,账户最后的余额应该还是10000,但代码执行后的结果并不是这样。大家可以执行下面的代码看下结果。

public static final int THREAD_COUNT = 100;

    public static void main(String[] args) {
BankAccount myAccount = new BankAccount("accountOfMG", 10000.00);
for (int i = 0; i < THREAD_COUNT; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
int var = new Random().nextInt(100);
Thread.sleep(var);
} catch (InterruptedException e) {
e.printStackTrace();
}
double deposit = myAccount.deposit(1000.00);
System.out.println(Thread.currentThread().getName() + " balance1:" + deposit);
}
}).start();
}
for (int i = 0; i < THREAD_COUNT; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
int var = new Random().nextInt(100);
Thread.sleep(var);
} catch (InterruptedException e) {
e.printStackTrace();
}
double deposit = myAccount.withdraw(1000.00);
System.out.println(Thread.currentThread().getName() + " balance2:" + deposit); }
}).start();
}
} private static class BankAccount { Semaphore semaphore = new Semaphore(1); String accountName;
double balance; public BankAccount(String accountName, double balance) {
this.accountName = accountName;
this.balance = balance;
} public double deposit(double amount) {
try {
semaphore.acquire();
balance = balance + amount;
return balance;
} catch (Exception e) {
throw new RuntimeException("中断...");
} finally {
semaphore.release();
}
} public double withdraw(double amount) {
try {
semaphore.acquire();
balance = balance - amount;
return balance;
} catch (Exception e) {
throw new RuntimeException("中断...");
} finally {
semaphore.release();
}
} }

这里Semaphore并不能实现synchronized的功能的原因是:Semaphore并不能保证共享变量的可见性。

实现原理

Semaphore底层原理还是基于AQS机制的。这边就不具体分析了,感兴趣的可以参考我前面关于AQS的文章

简单总结

  • Semaphore只能用来做线程同步——控制线程的执行顺序,但是并不能保证线程安全;
  • Semaphore主要用来控制线程的并发数量,通常用在限流组件中。
  • Semaphore基于AQS机制实现。

并发工具类——Semaphore的更多相关文章

  1. 30行自己写并发工具类(Semaphore, CyclicBarrier, CountDownLatch)是什么体验?

    30行自己写并发工具类(Semaphore, CyclicBarrier, CountDownLatch)是什么体验? 前言 在本篇文章当中首先给大家介绍三个工具Semaphore, CyclicBa ...

  2. Java并发工具类Semaphore应用实例

    package com.thread.test.thread; import java.util.Random; import java.util.concurrent.*; /** * Semaph ...

  3. Java多线程并发工具类-信号量Semaphore对象讲解

    Java多线程并发工具类-Semaphore对象讲解 通过前面的学习,我们已经知道了Java多线程并发场景中使用比较多的两个工具类:做加法的CycliBarrier对象以及做减法的CountDownL ...

  4. 【重学Java】多线程进阶(线程池、原子性、并发工具类)

    线程池 线程状态介绍 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态.线程对象在不同的时期有不同的状态.那么Java中的线程存在哪几种状态呢?Java中的线程 状态被定 ...

  5. 并发工具类:CountDownLatch、CyclicBarrier、Semaphore

    在多线程的场景下,有些并发流程需要人为来控制,在JDK的并发包里提供了几个并发工具类:CountDownLatch.CyclicBarrier.Semaphore. 一.CountDownLatch ...

  6. Java中的4个并发工具类 CountDownLatch CyclicBarrier Semaphore Exchanger

    在 java.util.concurrent 包中提供了 4 个有用的并发工具类 CountDownLatch 允许一个或多个线程等待其他线程完成操作,课题点 Thread 类的 join() 方法 ...

  7. Java中的并发工具类(CountDownLatch、CyclicBarrier、Semaphore、Exchanger)

    在JDK的并发包里提供了很多有意思的并发工具类.CountDownLatch.CyclicBarrier和Semaphore 工具类提供了一种并发流程控制的手段,Exchanger 工具类则提供了在线 ...

  8. Java并发(十五):并发工具类——信号量Semaphore

    先做总结: 1.Semaphore是什么? Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源. 把它比作是控制流量的红绿灯,比如XX马路要 ...

  9. 25.大白话说java并发工具类-CountDownLatch,CyclicBarrier,Semaphore,Exchanger

    1. 倒计时器CountDownLatch 在多线程协作完成业务功能时,有时候需要等待其他多个线程完成任务之后,主线程才能继续往下执行业务功能,在这种的业务场景下,通常可以使用Thread类的join ...

随机推荐

  1. python输出中文乱码

    首选项-浏览插件目录-USER文件夹 找到编译环境设置,编译方式为UTF-8编码 { "cmd": ["c:/Python36/python.exe",&quo ...

  2. Python的炫技操作:条件语句的七种写法

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: Python极客社区 PS:如有需要Python学习资料的小伙伴可以 ...

  3. B. 复读机的力量

    我们规定一个人是复读机当且仅当他说的每一句话都是复读前一个人说的话. 我们规定一个人是复读机当且仅当他说的每一句话都是复读前一个人说的话. 我们规定一个人是复读机当且仅当他说的每一句话都是复读前一个人 ...

  4. 小L的直线

    小学时期的小L发现自己很有艺术细胞,于是买了一块画板,但是他的绘画水平使得他只能连接两点画出一条线段.有一天他决定在一张有n个点的图上作画,即他可以把这n个点任意连接.大家认为平行线是非常不美观的,于 ...

  5. Cucumber(3)——命令以及日志

    目录 回顾 基本执行命令 关于日志的生成 回顾 在上一节中,我介绍了cucumber一些基本的语法内容,如果你还没有进行相关的了解或者环境的配置,你可以点击这里来进行了解一下 在本节中,我会对cucu ...

  6. OkHttp 优雅封装 HttpUtils 之 气海雪山初探

    曾经在代码里放荡不羁,如今在博文中日夜兼行,只为今天与你分享成果.如果觉得本文有用,记得关注我,我将带给你更多. 介绍 HttpUtils 是近期开源的对 OkHttp 轻量封装的框架,它独创的异步预 ...

  7. Mysql数据导入导出功能(设置及使用)

    使用Mysql自带的outfile语法,将查询结果导成excel格式. 1.OUTFILE介绍及常见问题解决: )查询数据导出成csv 直接使用mysql导出csv方法 我们可以使用 into out ...

  8. 视频+图文 String类干货向总结

    目录 一.字符串的介绍及视频讲解 二.字符串的两种创建方式 方法一:通过new创建 方法二:直接创建 三.字符串的长度获取:length()方法 四.使用 == 和equals()方法比较两个字符串 ...

  9. #4018. 统计n! 尾部零

    题目出处: http://www.51cpc.com/problem/4018 题目描述 试统计正整数n的阶乘n!=1×2×3×…×n尾部连续零的个数. 输入格式 输入正整数n 输出格式 输出个数 样 ...

  10. IO多路复用小故事

    背景故事 小王住在某城市, 生活并长大. 最近, 小城引进了一个企业, 邮局. 这个邮局可了不得, 只要你花上几角钱, 就可以将一封信送到千里之外的朋友手中. 小王也趁机体验了一把, 得劲. 这天, ...