Java高并发编程中Semaphore的使用及详细介绍-刘宇
作者:刘宇
CSDN博客地址:https://blog.csdn.net/liuyu973971883
有部分资料参考,如有侵权,请联系删除。如有不正确的地方,烦请指正,谢谢。
一、什么是Semaphore?
Semaphore 是 synchronized 的加强版,可以控制线程的并发数量,而单纯的synchronized是无法做到这一点的。例如,实现一个文件允许的并发访问数。
二、常用方法
1、构造函数
- permits:许可数量
- fair:是否公平,如果是公平的则按照等待队列中的顺序获取,反之不一定;,不公平比公平的效率通常要高
//构造函数1,初始化可用许可数量,默认不公平
public Semaphore(int permits)
//构造函数2
public Semaphore(int permits, boolean fair)
2、acquire方法
- 从此信号量中获取许可,可以被打断
//获取一个许可,在提供一个许可前一直将线程阻塞,除非线程被中断。
public void acquire() throws InterruptedException
//获取permits个许可,在提供许可前一直将线程阻塞,除非线程被中断。
public void acquire(int permits) throws InterruptedException
3、release方法
- 释放许可
注:如果释放的比获取的许可多,那么总的许可也会增加,所有一定要拿多少释放多少。
//释放一个许可。
public void release()
//释放permits个许可。
public void release(int permits)
4、acquireUninterruptibly方法
- 获取许可,不可以被打断
//获取一个许可。
public void acquireUninterruptibly()
//获取permits个许可。
public void acquireUninterruptibly(int permits)
5、getQueueLength方法
- 获取有多少个线程处于等待队列中
//获取等待队列长度。
public final int getQueueLength()
6、availablePermits方法
- 获取当前有多少个可用许可
//获取当前可以许可数
public int availablePermits()
7、hasQueuedThreads方法
- 判断是否由线程处于等待状态
//判断是否由线程处于等待状态
public final boolean hasQueuedThreads()
8、getQueuedThreads方法
- 获取等待许可队列中的线程
- 虽然他是protected保护的,但是我们可以通过自定义一个类来继承它来获取到
//获取等待许可队列中的线程
protected Collection<Thread> getQueuedThreads()
9、tryAcquire方法
- 尝试获取许可,如果没有获取到直接返回,返回值为boolean
//尝试获取1个许可
public boolean tryAcquire()
//尝试在特定时间内获取1个许可,可中断
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException
//尝试获取permits个许可
public boolean tryAcquire(int permits)
//尝试在特定时间内获取permits个许可,可中断
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException
三、利用Semaphore自定义锁
package com.brycen.concurrency03.semaphore;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreExample1 {
public static void main(String[] args) {
final MyLock myLock = new MyLock();
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+" is running");
myLock.lock();
System.out.println(Thread.currentThread().getName()+" get the lock");
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
myLock.unLock();
}
System.out.println(Thread.currentThread().getName()+" release lock");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+" is running");
myLock.lock();
System.out.println(Thread.currentThread().getName()+" get the lock");
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
myLock.unLock();
}
System.out.println(Thread.currentThread().getName()+" release lock");
}
}).start();
}
static class MyLock{
//实例化semaphore对象,设置1个许可
final Semaphore semaphore = new Semaphore(1);
public void lock() throws InterruptedException {
//获取一个许可
semaphore.acquire();
}
public void unLock() {
//释放一个许可
semaphore.release();
}
}
}
运行结果:
Thread-0 is running
Thread-1 is running
Thread-0 get the lock
Thread-0 release lock
Thread-1 get the lock
Thread-1 release lock
四、简单案例
1、可打断的acquire
package com.brycen.concurrency03.semaphore;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreExample2 {
public static void main(String[] args) throws InterruptedException {
final Semaphore semaphore = new Semaphore(2);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+" is running");
//获取一个许可
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+" get the permits");
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放一个许可
semaphore.release();
}
System.out.println(Thread.currentThread().getName()+" finish");
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+" is running");
//获取一个许可
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+" get the permits");
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放一个许可
semaphore.release();
}
System.out.println(Thread.currentThread().getName()+" finish");
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+" is running");
//获取一个许可
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+" get the permits");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放一个许可
semaphore.release();
}
System.out.println(Thread.currentThread().getName()+" finish");
}
});
t1.start();
TimeUnit.MILLISECONDS.sleep(50);
t2.start();
//进行休眠,确保T1、T2获取到许可
TimeUnit.MILLISECONDS.sleep(50);
t3.start();
//进行休眠,确保T3执行到acquire方法
TimeUnit.MILLISECONDS.sleep(50);
//将T3线程进行打断
t3.interrupt();
}
}
输出结果:
Thread-0 is running
Thread-0 get the permits
Thread-1 is running
Thread-1 get the permits
Thread-2 is running
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1302)
at java.util.concurrent.Semaphore.acquire(Semaphore.java:312)
at com.brycen.concurrency03.semaphore.SemaphoreExample2$3.run(SemaphoreExample2.java:52)
at java.lang.Thread.run(Thread.java:745)
Thread-2 finish
Thread-0 finish
Thread-1 finish
2、不可打断的acquire、获取可用许可、获取等待队列的大小
package com.brycen.concurrency03.semaphore;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreExample3 {
public static void main(String[] args) throws InterruptedException {
final Semaphore semaphore = new Semaphore(2);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+" is running");
//获取一个许可
semaphore.acquireUninterruptibly();
System.out.println(Thread.currentThread().getName()+" get the permits");
TimeUnit.SECONDS.sleep(5);
}catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
//释放一个许可
semaphore.release();
}
System.out.println(Thread.currentThread().getName()+" finish");
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+" is running");
//获取一个许可
semaphore.acquireUninterruptibly();
System.out.println(Thread.currentThread().getName()+" get the permits");
TimeUnit.SECONDS.sleep(5);
}catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
//释放一个许可
semaphore.release();
}
System.out.println(Thread.currentThread().getName()+" finish");
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+" is running");
//获取一个许可
semaphore.acquireUninterruptibly();
System.out.println(Thread.currentThread().getName()+" get the permits");
} finally {
//释放一个许可
semaphore.release();
}
System.out.println(Thread.currentThread().getName()+" finish");
}
});
t1.start();
TimeUnit.MILLISECONDS.sleep(50);
//获取当前可用许可
System.out.println("当前可用许可:"+semaphore.availablePermits());
//获取当前等待队列数
System.out.println("当前等待队列数:"+semaphore.getQueueLength());
t2.start();
//进行休眠,确保T1、T2获取到许可
TimeUnit.MILLISECONDS.sleep(50);
//获取当前可用许可
System.out.println("当前可用许可:"+semaphore.availablePermits());
//获取当前等待队列数
System.out.println("当前等待队列数:"+semaphore.getQueueLength());
t3.start();
//进行休眠,确保t3进入acquireUninterruptibly方法
TimeUnit.MILLISECONDS.sleep(50);
//将T3线程进行打断,此处无效,因为我们调用的是acquireUninterruptibly
t3.interrupt();
//获取当前可用许可
System.out.println("当前可用许可:"+semaphore.availablePermits());
//获取当前等待队列数
System.out.println("当前等待队列数:"+semaphore.getQueueLength());
}
}
输出结果:
Thread-0 is running
Thread-0 get the permits
当前可用许可:1
当前等待队列数:0
Thread-1 is running
Thread-1 get the permits
当前可用许可:0
当前等待队列数:0
Thread-2 is running
当前可用许可:0
当前等待队列数:1
Thread-0 finish
Thread-2 get the permits
Thread-2 finish
Thread-1 finish