信号量(Semaphore)
-
概述:对资源有限的场景做流量控制
-
主要是用来做流控的,协调线程,完成功能
-
场景:实现数据库连接池
-
示意图:
-
-
源码分析:
-
构造实参:整数
-
近似理解一个许可证,当线程需要执行一个方法的时候,首先调用acquire方法,拿到一个许可证,然后做子线程自己的业务:
- 这个只是做许可证的流量控制
-
执行自己的业务逻辑
-
执行完后,调用release,归还许可证:拿给其他线程用
-
-
假设只有4个许可证:
- 那么同时最多就只能有四个线程来执行任务,其他的线程需要等待归还许可证后才能执行自己的业务逻辑
- 这个就可以实现流控
-
-
实现数据库连接池
-
实现思路:使用信号量进行限流操作
-
设置两个信号量(useful与useless),当前可用连接数与当前已用连接数
- 如果不设置已用连接:连接数不受限制了,你来几个就给你几个
- 虽然先定义了许可证,但是在使用的时候,我们不调用acquire,而是直接调用这个release,这个时候如果new 出了一个信号量对象去调用(这种机制是被允许的)
- 这个时候就需要加一个已用连接数
-
为什么调用了信号量的acquire后,还要对数据库连接池加锁?
- 不是一个概念,信号量就只是做线程的流控,实际操作的时候还是对数据库加锁
- 其实也可以不用锁这个池子,将许可证设置为1,那么就只能有一个线程进行访问
- 也可以不显示使用synchronized,用线程安全的容器;
-
semaphore中的acquire与release
-
* 不要wait后notify/notifyAll了, * semaphore内部是已经实现了,等待通知的:这个是由AQS进行实现的 * 调用acquire:拿不到就wait * 调用release:发起通知,
-
-
-
实现代码
-
DBPoolSemaphore.java
package cn.enjoyedu.ch2.tools.semaphore; import java.sql.Connection; import java.util.LinkedList; import java.util.concurrent.Semaphore; /** *类说明:演示Semaphore用法,一个数据库连接池的实现; * 不要wait后notify/notifyAll了, * semaphore内部是已经实现了,等待通知的:这个是由AQS进行实现的 * 调用acquire:拿不到就wait * 调用release:发起通知, */ public class DBPoolSemaphore { private final static int POOL_SIZE = 10; //两个指示器,分别表示池子还有可用连接和已用连接 private final Semaphore useful,useless; //存放数据库连接的容器,采用LinkedList private static LinkedList<Connection> pool = new LinkedList<Connection>(); //初始化池 static { for (int i = 0; i < POOL_SIZE; i++) { pool.addLast(SqlConnectImpl.fetchConnection()); } } //数据库连接池构造 public DBPoolSemaphore() { this.useful = new Semaphore(10); this.useless = new Semaphore(0); } /*归还连接*/ public void returnConnect(Connection connection) throws InterruptedException { if(connection!=null) { System.out.println("当前有"+useful.getQueueLength()+"个线程等待数据库连接!!" +"可用连接数:"+useful.availablePermits()); //执行归还连接操作,请求归还的信号量 useless.acquire(); synchronized (pool) { pool.addLast(connection); } //执行归还连接操作,释放这个连接持有的信号量,释放许可证 useful.release(); } } /*从池子拿连接*/ public Connection takeConnect() throws InterruptedException { //先请求连接,没有信号量,当前线程就阻塞,有就证明拿到了 ,这个时候就锁这个pool useful.acquire(); Connection connection; synchronized (pool) { connection = pool.removeFirst(); } useless.release(); return connection; } }
-
AppTest
package cn.enjoyedu.ch2.tools.semaphore; import cn.enjoyedu.tools.SleepTools; import java.sql.Connection; import java.util.Random; /** *类说明:测试数据库连接池 */ public class AppTest { private static DBPoolSemaphore dbPool = new DBPoolSemaphore(); private static class BusiThread extends Thread{ @Override public void run() { Random r = new Random();//让每个线程持有连接的时间不一样 long start = System.currentTimeMillis(); try { Connection connect = dbPool.takeConnect(); System.out.println("Thread_"+Thread.currentThread().getId() +"_获取数据库连接共耗时【"+(System.currentTimeMillis()-start)+"】ms."); SleepTools.ms(100+r.nextInt(100));//模拟业务操作,线程持有连接查询数据 System.out.println("查询数据完成,归还连接!"); dbPool.returnConnect(connect); } catch (InterruptedException e) { } } } //50个线程并发去拿数据库连接池 public static void main(String[] args) { for (int i = 0; i < 50; i++) { Thread thread = new BusiThread(); thread.start(); } } }
-
运行截图:
-
-
-
Exchange
-
概述:两个线程之间的协作(数据交换),处理同种数据的交换工作
-
工作表现:
-
线程A执行,拿到数据后,调用exchange方法阻塞;等线程B准备好数据(执行业务),
线程调用exchange方法,因为之前的线程A是调用了exchange方法阻塞了的;此时两个线程进行数据交换;那么才继续执行
-
JDK内部保证,这线程安全
-
-