Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
Semaphore的主要方法摘要:
void acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
void release():释放一个许可,将其返回给信号量。
int availablePermits():返回此信号量中当前可用的许可数。
boolean hasQueuedThreads():查询是否有线程正在等待获取。
下面是一个例子:
实例1
package com.paincupid.springmvc.application.concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3);// 创建Semaphore信号量,初始化许可大小为3
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e2) {
e2.printStackTrace();
}
Runnable runnable = new Runnable() {
public void run() {
try {
sp.acquire();// 请求获得许可,如果有可获得的许可则继续往下执行,许可数减1。否则进入阻塞状态
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() + "进入,当前已有" + (3 - sp.availablePermits()) + "个并发");
try {
Thread.sleep((long) (Math.random() * 10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() + "即将离开");
sp.release();// 释放许可,许可数加1
// 下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
System.out.println("线程" + Thread.currentThread().getName() + "已离开,当前已有" + (3 - sp.availablePermits()) + "个并发");
}
};
service.execute(runnable);
}
}
}
结果
线程pool-1-thread-1进入,当前已有1个并发
线程pool-1-thread-2进入,当前已有2个并发
线程pool-1-thread-3进入,当前已有3个并发
线程pool-1-thread-2即将离开
线程pool-1-thread-4进入,当前已有3个并发
线程pool-1-thread-2已离开,当前已有3个并发
线程pool-1-thread-1即将离开
线程pool-1-thread-5进入,当前已有3个并发
线程pool-1-thread-1已离开,当前已有3个并发
线程pool-1-thread-4即将离开
线程pool-1-thread-4已离开,当前已有2个并发
线程pool-1-thread-6进入,当前已有3个并发
线程pool-1-thread-3即将离开
线程pool-1-thread-7进入,当前已有3个并发
线程pool-1-thread-3已离开,当前已有3个并发
线程pool-1-thread-6即将离开
线程pool-1-thread-6已离开,当前已有2个并发
线程pool-1-thread-8进入,当前已有3个并发
线程pool-1-thread-5即将离开
线程pool-1-thread-5已离开,当前已有3个并发
线程pool-1-thread-9进入,当前已有3个并发
线程pool-1-thread-9即将离开
线程pool-1-thread-10进入,当前已有3个并发
线程pool-1-thread-9已离开,当前已有3个并发
线程pool-1-thread-7即将离开
线程pool-1-thread-7已离开,当前已有2个并发
线程pool-1-thread-8即将离开
线程pool-1-thread-8已离开,当前已有1个并发
线程pool-1-thread-10即将离开
线程pool-1-thread-10已离开,当前已有0个并发
实例2
package com.paincupid.springmvc.application.concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。
*/
public class SemaphoreLockTest {
public static void main(String[] args) {
final Business business = new Business();
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
executor.execute(new Runnable() {
public void run() {
business.service();
}
}
);
}
executor.shutdown();
}
private static class Business {
private int count;
Lock lock = new ReentrantLock();
Semaphore sp = new Semaphore(1);
public void service() {
// lock.lock();
try {
sp.acquire(); // 当前线程使用count变量的时候将其锁住,不允许其他线程访问
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try {
count++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(count);
} catch (RuntimeException e) {
e.printStackTrace();
} finally {
// lock.unlock();
sp.release(); // 释放锁
}
}
}
}
结果
1
2
3
实例3
使用Semaphore实现简单的阻塞队列 @Description: @param @param args
package com.paincupid.springmvc.application.concurrent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
public class SemaphoreBlockingQueue {
/**
* @Title: 使用Semaphore实现简单的阻塞队列 @Description: @param @param args
* 设定文件 @return void 返回类型 @throws
*/
public static void main(String[] args) {
SemaphoreQueue queue = new SemaphoreQueue(20);
// 开始生产
Productor productor = new Productor(queue);
productor.setName("生产者");
productor.start();
// 开始消费
Cousumertor c1 = new Cousumertor(queue);
c1.setName("消费者-c1");
Cousumertor c2 = new Cousumertor(queue);
c2.setName("消费者-c2");
c1.start();
c2.start();
}
}
/**
* 队列 mutex相当于锁,用于控制非线程安全的valueList的操作
*/
class SemaphoreQueue {
private List<Integer> valueList;
private Semaphore putActionNum;// 可以进行put操作的许可数量
private Semaphore getActionNum;// 可以进行take操作的许可数量
private Semaphore mutex;
public SemaphoreQueue(int capacity) {
putActionNum = new Semaphore(capacity);// 维护队列大小
getActionNum = new Semaphore(0);// 初始化时,队列为空,put操作许可数量为0
mutex = new Semaphore(1);// 用于保护非线程安全的valueList操作,用于并发生产时控制
valueList = new ArrayList<Integer>(capacity);
}
public void put(Integer message) {
try {
putActionNum.acquire();// put操作许可减1
mutex.acquire();
valueList.add(message);
mutex.release();
getActionNum.release();// get操作许可加1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public Integer take() {
Integer message = null;
try {
getActionNum.acquire();// get操作许可减1
mutex.acquire();
if (valueList.size() > 0) {
message = valueList.get(0);
valueList.remove(0);
} else {
return null;
}
mutex.release();
putActionNum.release();// put操作许可加1
} catch (InterruptedException e) {
e.printStackTrace();
}
return message;
}
}
/**
* 生产者
*/
class Productor extends Thread {
SemaphoreQueue queue;
public Productor(SemaphoreQueue queue) {
this.queue = queue;
}
@SuppressWarnings("static-access")
public void run() {
int i = 0;
try {
while (true) {
i++;
Integer message = new Integer(i);
queue.put(message);
if (i % 20 == 0) {
System.out.println("======== " + this.getName() + " 累计生产了 " + i + " 条消息 =======");
Thread.currentThread().sleep(1000);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 消费者
*/
class Cousumertor extends Thread {
SemaphoreQueue queue;
public Cousumertor(SemaphoreQueue queue) {
this.queue = queue;
}
@SuppressWarnings("static-access")
public void run() {
try {
while (true) {
Integer message = queue.take();
if (message != null) {
System.out.println("======== " + this.getName() + " 消费消息:" + message + " =======");
}
Thread.currentThread().sleep(100);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果
======== 生产者 累计生产了 20 条消息 =======
======== 消费者-c1 消费消息:1 =======
======== 消费者-c2 消费消息:2 =======
======== 消费者-c2 消费消息:3 =======
======== 消费者-c1 消费消息:4 =======
======== 消费者-c1 消费消息:5 =======
======== 消费者-c2 消费消息:6 =======
======== 消费者-c1 消费消息:7 =======
======== 消费者-c2 消费消息:8 =======
======== 消费者-c2 消费消息:9 =======
======== 消费者-c1 消费消息:10 =======
======== 消费者-c2 消费消息:11 =======
======== 消费者-c1 消费消息:12 =======
======== 消费者-c1 消费消息:13 =======
======== 消费者-c2 消费消息:14 =======
======== 消费者-c2 消费消息:15 =======
======== 消费者-c1 消费消息:16 =======
======== 消费者-c2 消费消息:17 =======
======== 消费者-c1 消费消息:18 =======
======== 消费者-c1 消费消息:19 =======
======== 消费者-c2 消费消息:20 =======
======== 生产者 累计生产了 40 条消息 =======
======== 消费者-c2 消费消息:21 =======
======== 消费者-c1 消费消息:22 =======
======== 消费者-c1 消费消息:23 =======
======== 消费者-c2 消费消息:24 =======
======== 消费者-c1 消费消息:25 =======
======== 消费者-c2 消费消息:26 =======
======== 消费者-c1 消费消息:27 =======
======== 消费者-c2 消费消息:28 =======
======== 消费者-c2 消费消息:29 =======
======== 消费者-c1 消费消息:30 =======
======== 消费者-c2 消费消息:31 =======
======== 消费者-c1 消费消息:32 =======
======== 消费者-c2 消费消息:33 =======
======== 消费者-c1 消费消息:34 =======
======== 消费者-c1 消费消息:35 =======
======== 消费者-c2 消费消息:36 =======
======== 消费者-c1 消费消息:37 =======
======== 消费者-c2 消费消息:38 =======
======== 消费者-c2 消费消息:39 =======
======== 消费者-c1 消费消息:40 =======
======== 生产者 累计生产了 60 条消息 =======
======== 消费者-c2 消费消息:41 =======
======== 消费者-c1 消费消息:42 =======
======== 消费者-c2 消费消息:43 =======
======== 消费者-c1 消费消息:44 =======
======== 消费者-c2 消费消息:45 =======
======== 消费者-c1 消费消息:46 =======
======== 消费者-c2 消费消息:47 =======
======== 消费者-c1 消费消息:48 =======
======== 消费者-c2 消费消息:49 =======
======== 消费者-c1 消费消息:50 =======
======== 消费者-c2 消费消息:51 =======
======== 消费者-c1 消费消息:52 =======
======== 消费者-c1 消费消息:53 =======
======== 消费者-c2 消费消息:54 =======
======== 消费者-c2 消费消息:55 =======
======== 消费者-c1 消费消息:56 =======
======== 消费者-c2 消费消息:57 =======
======== 消费者-c1 消费消息:58 =======
======== 消费者-c2 消费消息:59 =======
======== 消费者-c1 消费消息:60 =======
======== 生产者 累计生产了 80 条消息 =======
======== 消费者-c2 消费消息:61 =======
======== 消费者-c1 消费消息:62 =======
======== 消费者-c1 消费消息:63 =======
======== 消费者-c2 消费消息:64 =======
======== 消费者-c2 消费消息:65 =======
======== 消费者-c1 消费消息:66 =======
======== 消费者-c2 消费消息:67 =======
======== 消费者-c1 消费消息:68 =======
======== 消费者-c1 消费消息:69 =======
======== 消费者-c2 消费消息:70 =======
======== 消费者-c2 消费消息:71 =======
======== 消费者-c1 消费消息:72 =======
======== 消费者-c2 消费消息:73 =======
======== 消费者-c1 消费消息:74 =======
======== 消费者-c1 消费消息:75 =======
======== 消费者-c2 消费消息:76 =======
======== 消费者-c1 消费消息:77 =======
======== 消费者-c2 消费消息:78 =======
======== 消费者-c2 消费消息:79 =======
======== 消费者-c1 消费消息:80 =======
======== 生产者 累计生产了 100 条消息 =======
======== 消费者-c1 消费消息:81 =======
======== 消费者-c2 消费消息:82 =======
======== 消费者-c1 消费消息:83 =======