1、简介:
Semaphore是Java的并发操作,用于多线程中,用于控制同时访问某个资源数,或某个操作的数量;
初始许可数可以通过new Semaphore的构造函数指定,release释放许可,acquire阻塞,如果没有释放,该方法会一直阻塞直到有许可,或操作超时
2、通过打印机的互斥demo来简单使用Semaphor,也能更加理解Semaphore
import java.util.concurrent.Semaphore;
/**
* semaphore的使用
*/
public class MutrxPrint {
/**
* 定义初始值为1的信号量(会先允许有一个线程执行,后来的会阻塞等待)
*/
private static final Semaphore mSemaphore = new Semaphore(1);
private static void print() throws InterruptedException {
System.out.println("print-----1");
//先等待,请求许可
mSemaphore.acquire();
System.out.println("print-----2");
System.out.println("print-----3--" + Thread.currentThread().getName() + "---Enter");
Thread.sleep(1000);
System.out.println("print-----4--" + Thread.currentThread().getName() + "---正在打印.....");
Thread.sleep(1000);
System.out.println("print-----5--" + Thread.currentThread().getName() + "---outer.....");
mSemaphore.release();
}
public static void main(String[] args){
//开启10个线程抢占
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
print();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
输出结果:
print-----3--Thread-1---Enter
print-----1
print-----1
print-----1
print-----1
print-----1
print-----1
print-----1
print-----1
print-----1
print-----4--Thread-1---正在打印.....
print-----5--Thread-1---outer.....
print-----2
print-----3--Thread-3---Enter
print-----4--Thread-3---正在打印.....
print-----5--Thread-3---outer.....
print-----2
print---1说明是其他线程进来,由于acquire方法在阻塞,
代码11行创建一个为1的许可数信号量,在调用print方法时代码17行会先判断是否还有剩余信号量许可数,有的话hui
会继续往下执行,没有的话会阻塞,等到其他线程执行完并释放,也就是代码24行
mSemaphore.release();之前阻塞的会继续执行
3、连接池的模拟实现
在项目中处理高并发时,一般数据库都会使用数据库连接池,假设现在数据库连接池最大连接数为10,当10个连接都分配出去以后,现在有用户继续请求连接,可能的处理:
a、手动抛出异常,用户界面显示,服务器忙,稍后再试
b、阻塞,等待其他连接的释放
从用户体验上来说,更好的选择当然是阻塞,等待其他连接的释放,用户只会觉得稍微慢了一点,并不影响他的操作。下面使用Semaphore模拟实现一个数据库连接池:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
/**
* 连接池使用Semaphore
*/
public class ConnectPool {
class Conn{}
private List<Conn> pools = new ArrayList<>(3);
private Semaphore mSemaphore = new Semaphore(3);
public ConnectPool() {
pools.add(new Conn());
pools.add(new Conn());
pools.add(new Conn());
}
/**
* 获取连接池的一个连接
* @return 连接池
*/
private Conn getConn() throws InterruptedException {
mSemaphore.acquire();
System.out.println("------getConn--1--" + Thread.currentThread().getName());
Conn c = null;
//加这个同步的原因是为了防止两个线程同时走到这里,加入sync就可以防止将同一个conn 同时分配给这两个线程了
synchronized (ConnectPool.class){
c = pools.remove(0);
}
System.out.println("------getConn--2--" + Thread.currentThread().getName() + "---" + c);
return c;
}
/**
*
* @param c 释放连接池中的连接
*/
private void releaseConn(Conn c){
pools.add(c);
System.out.println(Thread.currentThread().getName()+" release a conn " + c);
mSemaphore.release();
}
public static void main(String[] args){
ConnectPool mConnectPools = new ConnectPool();
new Thread(new Runnable() {
@Override
public void run() {
try {
Conn conn = mConnectPools.getConn();
Thread.sleep(3000);
mConnectPools.releaseConn(conn);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
for (int i = 0; i < 3; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Conn conn = mConnectPools.getConn();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
输出日志:
------getConn--1--Thread-1
------getConn--2--Thread-1---com.fengyu.cn.cha11.ConnectPool$Conn@2e4e6427
------getConn--1--Thread-3
------getConn--2--Thread-3---com.fengyu.cn.cha11.ConnectPool$Conn@1232cc0
------getConn--1--Thread-0
------getConn--2--Thread-0---com.fengyu.cn.cha11.ConnectPool$Conn@2eb0c802
Thread-0 release a conn com.fengyu.cn.cha11.ConnectPool$Conn@2eb0c802
------getConn--1--Thread-2
------getConn--2--Thread-2---com.fengyu.cn.cha11.ConnectPool$Conn@2eb0c802
测试时,让线程Thread-1获得连接池一个3秒的连接,同时又有3个线程去连接池获取连接,由于连接池中只有2个连接,所有有一个线程会等待,直到Thread-1释放连接池中的连接,他才会跟连接池连接上;
好了文章有点啰嗦,有错误;
文章转载:
因篇幅问题不能全部显示,请点此查看更多更全内容