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

一、概念理解

官方是这样解释的:

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

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

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

图片

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

二、代码使用

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

我们先看Test类:

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

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

 1public class Student extends Thread {
2    private Semaphore sp =null;
3    private String name = null;
4    public Student(Semaphore sp, String name) {
5        this.sp = sp;
6        this.name = name;
7    }
8    @Override
9    public void run() {
10        try {
11            sp.acquire();
12            System.out.println(name+"拿到了打饭的许可");
13            TimeUnit.SECONDS.sleep(3);  
14        } catch (InterruptedException e) {
15            e.printStackTrace();
16        }finally {
17            System.out.println(name+"打好了饭,释放这个窗口");
18            sp.release();
19        }   
20    }
21}

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

图片

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

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

 1    @Override
2    public void run() {
3        try {
4            System.out.println(name+"进入了餐厅");
5            sp.acquire();
6            System.out.println(name+"拿到了打饭的许可");
7            TimeUnit.SECONDS.sleep(3);  
8        } catch (InterruptedException e) {
9            e.printStackTrace();
10        }finally {
11            System.out.println(name+"打好了饭,释放这个窗口");
12            sp.release();
13        }   
14    }

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

图片

结果很清晰,所以对于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完成的,篇幅问题在这里不赘述了。感谢各位支持。


更多相关文章

  1. Java线程之线程的调度-让步
  2. java中的几个线程池的使用
  3. (不谈废话,只有干货)解决线程间协作问题的工具类Exchanger详解
  4. 一文看懂 Node.js 中的多线程和多进程[每日前端夜话0x107]
  5. 如何停止一个线程池?
  6. Java 中线程池包含哪些状态?
  7. 如何创建、启动 Java 线程?
  8. Executors如何创建线程池?
  9. 什么是线程?什么是进程?为什么要有线程?有什么关系与区别?

随机推荐

  1. 关于获取Android中文件内容有感
  2. 杂谈:2012年Android的发展状况
  3. 王家林的81门一站式云计算分布式大数据&
  4. 高焕堂:iPhone软件是芝麻,巧用Android才能
  5. Android程序开发之数据存储(一): 使用sqlite
  6. Android(安卓)EditText 状态切换
  7. PC监控通过网络数据监控ANDROID屏幕
  8. Android线程模型解析(包括UI的更新)
  9. Android程序开发的环境配置
  10. android 返回键与finish区别 onBackPress