1.定义
Semaphore是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态
2. 适用场景
- 保护一个重要[代码]部分 防止一次超过N个线程进入
- 在两个线程之间发送信号
3. 例子
Lock
package com.hqq.concurrency.semaphone;
/**
* Lock
* Created by heqianqian on 2017/7/30.
*/
public interface Lock {
void release() throws InterruptedException;
void acquire() throws InterruptedException;
}
Semaphore
package com.hqq.concurrency.semaphone;
/**
* Semaphore
* Created by heqianqian on 2017/7/30.
*/
public class Semaphore implements Lock {
private final int licenses;
private int counter;
public Semaphore(int counter) {
this.counter = counter;
this.licenses = counter;
}
public int getNumLicenses() {
return this.licenses;
}
public int getAvailableLicenses() {
return counter;
}
@Override
public synchronized void acquire() throws InterruptedException {
while (counter == 0) {
wait();
}
counter = counter - 1;
}
@Override
public synchronized void release(){
if (counter < licenses) {
counter = counter + 1;
notify();
}
}
}
FruitType:
package com.hqq.concurrency.semaphone;
/**
* FruitType
* 水果类型
* Created by heqianqian on 2017/7/30.
*/
public enum FruitType {
APPLE, ORANGE, BANANA;
}
Fruit
package com.hqq.concurrency.semaphone;
/**
* Fruit
* <p>
* Created by heqianqian on 2017/7/30.
*/
public class Fruit {
private FruitType fruitType;
public Fruit(FruitType fruitType) {
this.fruitType = fruitType;
}
public FruitType getFruitType() {
return fruitType;
}
@Override
public String toString() {
switch (fruitType) {
case ORANGE:
return "Orange";
case APPLE:
return "Apple";
case BANANA:
return "Banana";
default:
return "";
}
}
}
FruitBowl
package com.hqq.concurrency.semaphone;
import java.util.ArrayList;
import java.util.List;
/**
* FruitBowl
* Created by heqianqian on 2017/7/30.
*/
public class FruitBowl {
private List<Fruit> fruitList = new ArrayList<>();
public int countFruit() {
return fruitList.size();
}
public void put(Fruit fruit) {
fruitList.add(fruit);
}
public Fruit get() {
if (fruitList.size() == 0) {
return null;
}
return fruitList.remove(0);
}
}
FruitShop
package com.hqq.concurrency.semaphone;
/**
* FruitShop
* Created by heqianqian on 2017/7/30.
*/
public class FruitShop {
private FruitBowl[] fruitBowls = {
new FruitBowl(),
new FruitBowl(),
new FruitBowl()
};
private boolean[] available = {
true,
true,
true
};
private Semaphore semaphore;
public FruitShop() {
for (int i = 0; i < 100; i++) {
fruitBowls[0].put(new Fruit(FruitType.APPLE));
fruitBowls[0].put(new Fruit(FruitType.BANANA));
fruitBowls[0].put(new Fruit(FruitType.ORANGE));
}
semaphore = new Semaphore(3);
}
public synchronized int countFruit() {
return fruitBowls[0].countFruit() + fruitBowls[1].countFruit()
+ fruitBowls[2].countFruit();
}
public synchronized FruitBowl takeBowl() {
FruitBowl fruitBowl = null;
try {
semaphore.acquire();
if (available[0]) {
fruitBowl = fruitBowls[0];
available[0] = false;
} else if (available[1]) {
fruitBowl = fruitBowls[1];
available[1] = false;
} else if (available[2]) {
fruitBowl = fruitBowls[2];
available[2] = false;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
return fruitBowl;
}
public synchronized void returnBowl(FruitBowl fruitBowl) {
if (fruitBowl == fruitBowls[0]) {
available[0] = true;
} else if (fruitBowl == fruitBowls[1]) {
available[1] = true;
} else if (fruitBowl == fruitBowls[2]) {
available[2] = true;
}
}
}
Customer
package com.hqq.concurrency.semaphone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Customer
* Created by heqianqian on 2017/7/30.
*/
public class Customer extends Thread {
private static final Logger LOGGER = LoggerFactory.getLogger(Customer.class);
private String name;
private FruitShop fruitShop;
private FruitBowl fruitBowl;
public Customer(String name, FruitShop fruitShop) {
this.name = name;
this.fruitShop = fruitShop;
this.fruitBowl = new FruitBowl();
}
@Override
public void run() {
while (fruitShop.countFruit() > 0) {
FruitBowl bowl = fruitShop.takeBowl();
Fruit fruit;
if (bowl != null && (fruit = bowl.get()) != null) {
LOGGER.info("{} took an {}", name, fruit);
fruitBowl.put(fruit);
fruitShop.returnBowl(bowl);
}
}
LOGGER.info("{} took {}", name, fruitBowl);
}
}
App
package com.hqq.concurrency.semaphone;
/**
* App
* Created by heqianqian on 2017/7/30.
*/
public class App {
/**
* main method
*
* @param args
*/
public static void main(String[] args) {
FruitShop shop = new FruitShop();
new Customer("Peter", shop).start();
new Customer("Paul", shop).start();
new Customer("Mary", shop).start();
new Customer("John", shop).start();
new Customer("Ringo", shop).start();
new Customer("George", shop).start();
}
}
输出结果:
INFO [2017-08-09 01:51:34,148] com.hqq.concurrency.semaphone.Customer: Paul took an Apple
INFO [2017-08-09 01:51:34,148] com.hqq.concurrency.semaphone.Customer: Paul took an Banana
INFO [2017-08-09 01:51:34,148] com.hqq.concurrency.semaphone.Customer: Mary took an Orange
INFO [2017-08-09 01:51:34,148] com.hqq.concurrency.semaphone.Customer: Paul took an Apple
INFO [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Mary took an Banana
INFO [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Paul took an Orange
INFO [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Mary took an Apple
INFO [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Paul took an Banana
INFO [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Mary took an Orange
INFO [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Paul took an Apple
INFO [2017-08-09 01:51:34,164] com.hqq.concurrency.semaphone.Customer: Mary took an Banana
...