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释放连接池中的连接,他才会跟连接池连接上;
好了文章有点啰嗦,有错误希望各位提出一起探讨,学习;
文章转载:https://blog.csdn.net/lmj623565791/article/details/26810813