Semaphore的使用

Semaphore的使用

关注

Semaphore 简介

本文将对Semaphore类中的全部方法进行案例式的实验,这样可以全面地了解此类提供类哪些核心功能。

此类的主要作用就是限制线程并发的数量,如果不限制线程并发的数量,CPU资源很快就会被耗尽,每个线程执行的任务是相当缓慢,因为CPU把时间片分配给不同的线程对象,而且上下文切换也要耗时,最终造成系统运行效率大幅降低,所以限制并发线程的数量还是非常有必要的。

Semaphore的同步性

类Semaphore所提供的功能完全就是sybchronized关键字的升级版,但它提供的功能更加的强大与方便,主要作用就是控制线程并发的数量,而这一点,单纯地使用synchronized是做不到的。

我们首先通过一个初步的案例来看看Semaphore类是如何实现限制线程并发数的。

Semaphore的使用

图1

Thread-4 1522991382314开始执行!

Thread-4 1522991383318执行结束!

Thread-3 1522991383319开始执行!

Thread-3 1522991384324执行结束!

Thread-0 1522991384325开始执行!

Thread-0 1522991385327执行结束!

Thread-1 1522991385327开始执行!

Thread-1 1522991386329执行结束!

Thread-2 1522991386329开始执行!

Thread-2 1522991387331执行结束!

类Semaphore的构造函数参数permits是许可的意思,代表同一时间内,最多允许多少个线程同时执行acquire()和release()之间的代码。所以上面的示例中,最多允许1个线程执行acquire()和release()之间的代码,所以打印的结果就是5个线程是同步的。

其实还可以传入>1的许可,代表同一时间内,最多允许x个线程可以执行acquire()和release()之间的代码。

如果将上面示例中Semaphore的构造函数参数permits改为2:

Semaphore的使用

图2

执行结果:

Thread-0 1522991932240开始执行!

Thread-4 1522991932240开始执行!

Thread-4 1522991933244执行结束!

Thread-0 1522991933244执行结束!

Thread-3 1522991933245开始执行!

Thread-1 1522991933245开始执行!

Thread-3 1522991934248执行结束!

Thread-1 1522991934248执行结束!

Thread-2 1522991934248开始执行!

Thread-2 1522991935253执行结束!

需要说明一下,对Semaphore类的构造方法传递的参数permits值如果大于1时,该类并不能保证线程安全性,因为还是有可能出现多个线程共同访问实例变量,导致出现脏数据的情况。

方法acquire(int permits)参数作用及动态添加permits许可数量

有参方法acquire(int permits)功能是没调用一次此方法,就使用x个许可。无参的acquire()作用是使用1个许可。

Semaphore的使用

图3

执行结果:

Thread-2 1522993586620开始执行!

Thread-9 1522993586620开始执行!

Thread-6 1522993586620开始执行!

Thread-0 1522993586620开始执行!

Thread-1 1522993586620开始执行!

Thread-2 1522993587096执行结束!

Thread-8 1522993587097开始执行!

Thread-0 1522993587212执行结束!

Thread-8 1522993587212执行结束!

Thread-5 1522993587213开始执行!

Thread-3 1522993587212开始执行!

Thread-6 1522993587361执行结束!

Thread-4 1522993587362开始执行!

Thread-9 1522993587531执行结束!

Thread-7 1522993587531开始执行!

Thread-1 1522993587557执行结束!

Thread-3 1522993587949执行结束!

Thread-4 1522993587991执行结束!

Thread-5 1522993588139执行结束!

Thread-7 1522993588158执行结束!

代码中有10个许可,每次执行代码semaphore.acquire(2);时消耗掉2个,所有10/2=5,说明同一时间只有5个线程允许执行acquire()和release()之间的代码。

Semaphore类构造参数new Semaphore(5);中的5并不是最终的许可数,仅仅是初始的状态值。release(int permits),可以动态添加permits的个数,无参release()作用是添加1个许可。

Semaphore的使用

图4

执行结果:

0

6

10

方法availablePermits()和drainPermits()

availablePermits()返回此Semaphore对象中当前可用的许可数,此方法通常用于调试,因为许可的数量可能实时在改变,并不是固定的数量。

drainPermits()可获取并返回立即可用的所有许可个数,并且将可用许可置0。

Semaphore的使用

图5

执行结果:

3

3

0 0

方法acquireUninterruptibly()的使用

方法acquireUninterruptibly()的作用是使等待进入acquire()方法的线程,不允许被中断。

Semaphore的使用

图6

执行结果:

A开始执行!

main……end!

java.lang.InterruptedException

at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:998)

at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)

at java.util.concurrent.Semaphore.acquire(Semaphore.java:312)

at com.thread.Semaphore.SemaphoreTest$Service.test(SemaphoreTest.java:17)

at com.thread.Semaphore.SemaphoreTest$MyThread.run(SemaphoreTest.java:36)

A执行结束!

上面示例未使用acquireUninterruptibly()所以,在现在B等待进入许可的时候被成功中断了。如果将示例中代码改成使用acquireUninterruptibly():

semaphore.acquireUninterruptibly();

执行结果:

A开始执行!

main……end!

A执行结束!

B开始执行!

B执行结束!

acquireUninterruptibly()方法还有重载的写法acquireUninterruptibly(int permits),此方法的作用是在等待许可的情况下不允许中断,如果成功获得锁,则取得制定的permits许可个数。

公平与非公平信号量测试

所谓的公平信号量是获得锁的顺序与线程启动的顺序有关,但不代表100%地获得信号量,仅仅是在概率上能得到保证。而非公平信号量就是无关的量。

Semaphore的使用

图7

执行结果

Thread-2启动了!

Thread-0启动了!

Thread-1启动了!

Thread-4启动了!

Thread-3启动了!

Thread-2开始执行!

Thread-5启动了!

Thread-5开始执行!

Thread-1开始执行!

Thread-0开始执行!

Thread-4开始执行!

Thread-3开始执行!

更改Service代码如下:

private boolean isFair = true;

执行结果:

Thread-1启动了!

Thread-4启动了!

Thread-1开始执行!

Thread-2启动了!

Thread-0启动了!

Thread-3启动了!

Thread-5启动了!

Thread-4开始执行!

Thread-2开始执行!

Thread-0开始执行!

Thread-3开始执行!

Thread-5开始执行!

由此可见,非公平信号量允许的效果是线程启动的顺序与调用semaphore.acquire();的顺序无关,也就是线程先启动并不代表先获得许可。公平信号量的运行效果是线程启动的顺序与调用semaphore.acquire();的顺序有关,也就是先启动的线程优先获得许可。

方法tryAcquire()的使用

无参方法tryAcquire()的作用是尝试地获得1个许可,如果获取不到则返回false,此方法通常与if语句结合使用,其具有无阻塞的特点。无阻塞的特点可以使线程不至于在同步处一直持续等待的状态,如果if语句判断不成立则线程会继续走else语句,程序会继续向下运行。

Semaphore的使用

执行结果:

A开始执行!

B未成功进入!

  • tryAcquire(int permits)的作用是尝试地获得x个许可,如果获取不到则返回false。

  • tryAcquire(long timeout,TimeUnit unit)的作用是在指定的时间内尝试地获得1个许可,如果获取不到则返回false。

  • tryAcquire(int permits,long timeout,TimeUnit unit)的作用是在指定时间内尝试地获得x个许可,如果获取不到则返回false

Semaphore的使用

结束

绝对干货!!!持续更新!!!

如果您喜欢请加关注!!!


分享到:


相關文章: