详解java中的同步工具类Semaphore

简介: Semaphore是java并发包里面的一个工具类,我们限制可以访问某些资源的线程数目就可以使用Semaphore了。这篇文章将对Semaphore的概念和使用进行一个详解。

一、概念理解


官方是这样解释的:


Semaphore用于限制可以访问某些资源(物理或逻辑的)的线程数目,他维护了一个许可证集合,有多少资源需要限制就维护多少许可证集合,假如这里有N个资源,那就对应于N个许可证,同一时刻也只能有N个线程访问。一个线程获取许可证就调用acquire方法,用完了释放资源就调用release方法。


不过这样的解释实在有点抽象,现在用我自己的话来解释一下:


相信在学生时代都去餐厅打过饭,假如有3个窗口可以打饭,同一时刻也只能有3名同学打饭。第四个人来了之后就必须在外面等着,只要有打饭的同学好了,就可以去相应的窗口了。

v2-9a262c37203391ebfcbc137899a2842f_1440w.jpg

比如说这张图,就全是了Semaphore的基本使用。认识一个知识点的最好方式就是直接去使用,我们干脆直接上代码来看看如何使用。


二、代码使用


这个案例使用的就是我们之前的小例子,也就是去餐厅打饭的案例。

我们先看Test类:

public class SemaphoreTest {
    //第一步:定义一个信号量Semaphore
    static Semaphore sp = new Semaphore(3);
    public static void main(String[] args) {
        //第二步:定义10个学生去打饭
        for(int i=0;i<10;i++) {
            //十个学生用一个信号量
            new Student(sp, "学生"+i).start();
        }
    }
}

在这个代码中我们看到,主要是new了一个Semaphore,然后赋给每一位同学Student,接下来我们就来好好看看Student线程是如何实现的。

public class Student extends Thread {
    private Semaphore sp =null;
    private String name = null;
    public Student(Semaphore sp, String name) {
        this.sp = sp;
        this.name = name;
    }
    @Override
    public void run() {
        try {
            sp.acquire();
            System.out.println(name+"拿到了打饭的许可");
            TimeUnit.SECONDS.sleep(3);  
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println(name+"打好了饭,释放这个窗口");
            sp.release();
        }   
    }
}

在这个Student类中我们最主要看run方法的实现,首先我们通过acquire获取了当前窗口的许可,然后休眠3秒代表打饭,最后在finally使用release方法释放这个窗口许可证。代码很简单,原理很清楚,我们测试一波:

v2-18e3114aca2866a376cf63cd15ce83d9_1440w.jpg

这个结果你也看到了,基本上同一时刻只能有三个学生在窗口旁边。


在这里你可能有一个疑问了,Semaphore好像和synchronized关键字没什么区别,都可以实现同步,如果是这样那说明我们还没有真正理解jdk的注释,他只是限制了访问某些资源的线程数,其实并没有实现同步,我们可以看一下:

@Override
    public void run() {
        try {
            System.out.println(name+"进入了餐厅");
            sp.acquire();
            System.out.println(name+"拿到了打饭的许可");
            TimeUnit.SECONDS.sleep(3);  
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println(name+"打好了饭,释放这个窗口");
            sp.release();
        }   
    }

现在我们在获取许可前增加了一条输出语句,也就是能打印出有哪个线程进入了,再去测试一波:

v2-324e9c65814e4e7a2988430a2c97ede2_1440w.jpg

结果很清晰,所以对于Semaphore来说,我们需要记住的其实是资源的互斥而不是资源的同步,在同一时刻是无法保证同步的,但是却可以保证资源的互斥。


三、其他方法


在上面我们使用最基本的acquire方法和release方法就可以实现Semaphore最常见的功能,不过其他方法还是需要我们去了解一下的。


1、acquire(int permits)


从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。就好比是一个学生占两个窗口。这同时也对应了相应的release方法。


2、release(int permits)


释放给定数目的许可,将其返回到信号量。这个是对应于上面的方法,一个学生占几个窗口完事之后还要释放多少


3、availablePermits()


返回此信号量中当前可用的许可数。也就是返回当前还有多少个窗口可用。


4、reducePermits(int reduction)


根据指定的缩减量减小可用许可的数目。


5、hasQueuedThreads()


查询是否有线程正在等待获取资源。


6、getQueueLength()


返回正在等待获取的线程的估计数目。该值仅是估计的数字。


7、tryAcquire(int permits, long timeout, TimeUnit unit)


如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被中断,则从此信号量获取给定数目的许可。


8、acquireUninterruptibly(int permits)


从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。

基本上常见的使用方法都在这,Semaphore底层是由AQS和Uasafe完成的,篇幅问题在这里不赘述了。感谢各位支持。

相关文章
|
25天前
Mybatis+mysql动态分页查询数据案例——分页工具类(Page.java)
Mybatis+mysql动态分页查询数据案例——分页工具类(Page.java)
20 1
|
25天前
Mybatis+mysql动态分页查询数据案例——工具类(MybatisUtil.java)
Mybatis+mysql动态分页查询数据案例——工具类(MybatisUtil.java)
15 1
|
2天前
|
Java 编译器
Java Character 类
4月更文挑战第13天
|
2天前
|
存储 缓存 安全
Java并发基础之互斥同步、非阻塞同步、指令重排与volatile
在Java中,多线程编程常常涉及到共享数据的访问,这时候就需要考虑线程安全问题。Java提供了多种机制来实现线程安全,其中包括互斥同步(Mutex Synchronization)、非阻塞同步(Non-blocking Synchronization)、以及volatile关键字等。 互斥同步(Mutex Synchronization) 互斥同步是一种基本的同步手段,它要求在任何时刻,只有一个线程可以执行某个方法或某个代码块,其他线程必须等待。Java中的synchronized关键字就是实现互斥同步的常用手段。当一个线程进入一个synchronized方法或代码块时,它需要先获得锁,如果
21 0
|
2天前
|
存储 Java
Java基础教程(7)-Java中的面向对象和类
【4月更文挑战第7天】Java是面向对象编程(OOP)语言,强调将事务抽象成对象。面向对象与面向过程的区别在于,前者通过对象间的交互解决问题,后者按步骤顺序执行。类是对象的模板,对象是类的实例。创建类使用`class`关键字,对象通过`new`运算符动态分配内存。方法包括构造函数和一般方法,构造函数用于对象初始化,一般方法处理逻辑。方法可以有0个或多个参数,可变参数用`类型...`定义。`this`关键字用于访问当前对象的属性。
|
6天前
|
Java Shell
Java 21颠覆传统:未命名类与实例Main方法的编码变革
Java 21颠覆传统:未命名类与实例Main方法的编码变革
10 0
|
6天前
|
Java
Java 15 神秘登场:隐藏类解析未知领域
Java 15 神秘登场:隐藏类解析未知领域
10 0
|
8天前
|
安全 Java
append在Java中是哪个类下的方法
append在Java中是哪个类下的方法
21 9
|
9天前
|
JavaScript Java 测试技术
基于Java的网络类课程思政学习系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的网络类课程思政学习系统的设计与实现(源码+lw+部署文档+讲解等)
25 0
基于Java的网络类课程思政学习系统的设计与实现(源码+lw+部署文档+讲解等)
|
9天前
|
存储 安全 Java
java多线程之原子操作类
java多线程之原子操作类