多线程之信号量Semaphore及原理
来源:互联网 发布:正规淘宝刷销量平台 编辑:程序博客网 时间:2024/06/16 17:32
读前必看AQS原理——http://blog.csdn.net/qq_31957747/article/details/74910939
一、信号量(Semaphore)
重入锁ReentrantLock是独占模式,一次都只允许一个线程访问一个资源,而信号量是共享模式,也就是说可以指定多个线程,同时访问某一个资源。
Semaphore的两个构造方法:
public Semaphore(int permits) { sync = new NonfairSync(permits); }public Semaphore(int permits, boolean fair) { //第二个参数指定是否公平 sync = fair ? new FairSync(permits) : new NonfairSync(permits); }跟ReentrantLock一样,Semaphore也有公平版本和非公平版本,默认是非公平版本。
在构造信号量对象的时候,必须指定同时有多少线程可以访问某一个资源,也就是参数列表中的permits。
Semaphore的主要方法:
acquire()方法尝试获得一个准入的许可。若无法获得,则线程会等待,直到有线程释放一个许可或者当前线程被中断。
acquireUnterruptibly()跟acquire()方法差不多,不过不响应中断。
tryAcquire()尝试获得许可,如果成功返回true,失败立即返回false,不会等待。
tryAcquire(long timeout,TimeUnit unit)跟tryAcquire()方法差不多,不过失败会等待以timeout为数值,以unit为单位的时间数,超过时间返回false。
release()在线程访问资源结束后,释放一个许可,以使其他等待许可的线程可以进行资源访问。
看个例子:
public class SemapDemo implements Runnable { final Semaphore semaphore = new Semaphore(5); @Override public void run() { try { semaphore.acquire(); Thread.sleep(1000); System.out.println(Thread.currentThread().getId()+" :done!"); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { ExecutorService exec = Executors.newFixedThreadPool(20); final SemapDemo demo = new SemapDemo(); for(int i =0;i<20;i++){ exec.submit(demo); } exec.shutdown(); }}
观察这个程序的输出,你会发现以5个线程一组为单位,依次输出。也就验证了Semaphore是共享模式,接下来我们分析下Semaphore的源码,看看共享是体现在什么地方的。
二、Semahore源码分析:
我们就对acquire()和release()方法进行分析。
2.1 acquire方法
public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1); }
2.1.1 acquireSharedInterruptibly方法
public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) //如果剩余量小于0,这个线程节点就不能访问资源,在等待队列中等着吧;大于0的话,那当然acquire()下面的方法就执行了 doAcquireSharedInterruptibly(arg); }
2.1.1.1 tryAcquireShared方法在AQS中的定义是要由子类实现,这里我们就看非公平版本的。
protected int tryAcquireShared(int acquires) { return nonfairTryAcquireShared(acquires); }
final int nonfairTryAcquireShared(int acquires) { for (;;) { //自旋 int available = getState();//Semaphore的state就是permits,即还允许访问的资源数,这点跟ReentrantLock不太一样(ReentrantLock的state表示重入数) int remaining = available - acquires; //还允许访问的资源数-当前线程请求资源数 = 剩余允许允许访问的资源数 if (remaining < 0 || //剩余数<0 compareAndSetState(available, remaining))//剩余数大于0,且自旋的这个周期内资源没有被其他线程访问(CAS自旋保证了原子性,不成功继续自旋) return remaining; } }
2.1.1.2 doAcquireSharedInterruptibly方法
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
这个方法在AQS那篇文章分析过,这里就不做多介绍了,就是一个线程进入等待状态,等待被唤醒的过程。
2.2 release方法
public void release() { sync.releaseShared(1); }
2.2.1 releaseShared方法
public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { //尝试释放资源 doReleaseShared(); //唤醒后继资源 return true; } return false; }doReleaseShared()方法我们在AQS那篇文章中也已经介绍过,所以这边就分析Semaphore中的Sync重写的tryReleaseShared方法。
2.2.1.1 tryReleaseShared方法
protected final boolean tryReleaseShared(int releases) { for (;;) { //自旋 int current = getState(); int next = current + releases; //释放资源后的可访问的资源数 if (next < current) // 这里应该是整形溢出 throw new Error("Maximum permit count exceeded"); if (compareAndSetState(current, next))//这个自旋周期内资源没有被其他线程访问,就把state的值设置为next return true; } }分析到这,大家应该知道Semaphore的共享是怎么一回事儿了吧(当然把permits设置为1的话,又跟ReentrantLock相像了)。
三、Semaphore模拟实现一个连接池
public class ConnectPool { private int maxConnectNum; private List<Connect> pool; private Semaphore semaphore; public ConnectPool(int maxConnectNum){ this.maxConnectNum = maxConnectNum; pool = new ArrayList<>(maxConnectNum); semaphore = new Semaphore(maxConnectNum); for(int i = 0;i<maxConnectNum;i++){ pool.add(new Connect(this)); } } public void poolAdd(Connect c){ pool.add(c); } public void semaphoreRelease(){ semaphore.release(); } public Connect getConnection() throws InterruptedException { semaphore.acquire(); Connect c = pool.remove(0); System.out.println(Thread.currentThread().getId()+":拿到了一个连接"+c); return c; } public static void main(String[] args) { final ConnectPool pool = new ConnectPool(3); /** * 第一个线程占用1个连接3秒 */ new Thread() { public void run() { try { Connect c = pool.getConnection(); Thread.sleep(3000); c.close(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); /** * 开启3个线程请求分配连接 */ for (int i = 0; i < 3; i++) { new Thread() { public void run() { try { Connect c = pool.getConnection(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } }}
public class Connect { private ConnectPool connectPool; public Connect(ConnectPool connectPool){ this.connectPool = connectPool; } /** * 释放连接 */ public void close(){ connectPool.poolAdd(this); System.out.println(Thread.currentThread().getId()+":释放了一个连接"+this); connectPool.semaphoreRelease(); }}
运行结果:
当然只是模拟而已,跟实际的连接池出入应该很大。
阅读全文
0 0
- 多线程之信号量Semaphore及原理
- C# 多线程之信号量Semaphore
- Java多线程之信号量Semaphore
- Java 多线程之信号量 Semaphore
- java多线程之Semaphore信号量详解
- 我之见--java多线程信号量Semaphore
- 多线程之实现同步的信号量Semaphore
- java多线程之Semaphore信号量详解
- java多线程之Semaphore信号量详解
- Java多线程工具之Semaphore(信号量)
- C++多线程同步之Semaphore(信号量)
- java多线程之Semaphore信号量详解
- Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例 (r)
- Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例
- Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例
- C++多线程-信号量Semaphore
- JAVA多线程--信号量(Semaphore)
- JAVA多线程--信号量(Semaphore)
- [LintCode]165.合并两个排序链表
- php json_encode的选项JSON_UNESCAPED_UNICODE
- IO流-第十七天
- 数据结构之链表
- {"JSON":"P"}
- 多线程之信号量Semaphore及原理
- 辅助工具 2
- 鼠标光标更改总结
- 基于VUE+THINKPHP的项目实践
- jmeter自动化-传参
- <iOS和Unity交互>之界面跳转
- 织梦dedecms后台安全性设置大全
- eclipse maven项目中使用tomcat插件部署项目
- 算法(一)---数学基础知识