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

并发编程系列博客传送门


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. matlab操作Excel数据

    sheet是Excel的表格,xIRange是表格的列的范围 指定xlRange,例如使用语法'C1:C2',其中C1和C2是定义要读取的区百域的两个度相对的角. 例如,'D2:H4'表示工作表上的两 ...

  2. 学习Saleforce | 业内第一份Salesforce学习者数据报告

    自从自由侠部落这个Salesforce中文学习平台成立以来,我们接触到了越来越多的Salesforce的学习者,由衷感觉到这个学习生态圈愈发蓬勃发展. 为了了解Salesforce学习者的基本情况.现 ...

  3. Daily Scrum 1/5/2015

    Process: Zhaoyang: Fix some crash bugs and increase the program stability. Yangdong: Complete some b ...

  4. 详解 迭代器 —— Iterator接口、 ListIterator接口 与 并发修改异常

    (请关注 本人"Collection集合"博文--<详解 Collection集合>) Iterator接口(迭代器): 概述: 对 collection 进行迭代的迭 ...

  5. [V&N2020 公开赛] Web misc部分题解

    0x00 前言 写了一天题目,学到了好多东西, 简单记录一下 0x01 Web HappyCTFd 直接使用网上公开的cve打: 解题思路:先注册一个admin空格账号,注意这里的靶机无法访问外网,邮 ...

  6. hashlib的md5计算

    hashlib的md5计算 hashlib概述 涉及加密服务:Cryptographic Services 其中 hashlib是涉及 安全散列 和 消息摘要 ,提供多个不同的加密算法借口,如SHA1 ...

  7. 用functools.lru_cache实现Python的Memoization

    现在你已经看到了如何自己实现一个memoization函数,我会告诉你,你可以使用Python的functools.lru_cache装饰器来获得相同的结果,以增加方便性. 我最喜欢Python的原因 ...

  8. 用Python打造电脑人脸屏幕解锁神器附带接头暗号!

    前言 最近突然有个奇妙的想法,就是当我对着电脑屏幕的时候,电脑会先识别屏幕上的人脸是否是本人,如果识别是本人的话需要回答电脑说的暗语,答对了才会解锁并且有三次机会.如果都没答对就会发送邮件给我,通知有 ...

  9. c++<ctime>中常用函数

    先说一下c++标准库并没有提供所谓的日期类型,而是继承了c的日期类型 <cmath>里面有些常用的函数,比如计时函数clock().获取系统时间的函数time(),下面就具体的介绍一下 1 ...

  10. tp3.2的__construct和_initialize方法

    在tp3.2框架里面,有一个php自带的__construct()构造函数和tp3自带的构造函数_initialize()的实行顺序是先实行 php自带的__construct()构造函数 再实行 t ...