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