电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術(shù)文章
文章詳情頁

詳解Java 信號量Semaphore

瀏覽:26日期:2022-08-24 17:41:31

Semaphore也是一個同步器,和前面兩篇說的CountDownLatch和CyclicBarrier不同,這是遞增的,初始化的時候可以指定一個值,但是不需要知道需要同步的線程個數(shù),只需要在同步的地方調(diào)用acquire方法時指定需要同步的線程個數(shù);

一.簡單使用

同步兩個子線程,只有其中兩個子線程執(zhí)行完畢,主線程才會執(zhí)行:

package com.example.demo.study;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;public class Study0217 { //創(chuàng)建一個信號量的實例,信號量初始值為0 static Semaphore semaphore = new Semaphore(0); public static void main(String[] args) throws Exception { ExecutorService pool = Executors.newFixedThreadPool(3); pool.submit(()->{ System.out.println('Thread1---start'); //信號量加一 semaphore.release(); });pool.submit(()->{ System.out.println('Thread2---start'); //信號量加一 semaphore.release(); }); pool.submit(()->{ System.out.println('Thread3---start'); //信號量加一 semaphore.release(); }); //等待兩個子線程執(zhí)行完畢就放過,必須要信號量等于2才放過 semaphore.acquire(2); System.out.println('兩個子線程執(zhí)行完畢');//關(guān)閉線程池,正在執(zhí)行的任務繼續(xù)執(zhí)行 pool.shutdown(); }}

詳解Java 信號量Semaphore

這個信號量也可以復用,類似CyclicBarrier:

package com.example.demo.study;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;public class Study0217 { //創(chuàng)建一個信號量的實例,信號量初始值為0 static Semaphore semaphore = new Semaphore(0); public static void main(String[] args) throws Exception { ExecutorService pool = Executors.newFixedThreadPool(3); pool.submit(()->{ System.out.println('Thread1---start'); //信號量加一 semaphore.release(); });pool.submit(()->{ System.out.println('Thread2---start'); //信號量加一 semaphore.release(); });//等待兩個子線程執(zhí)行完畢就放過,必須要信號量等于2才放過 semaphore.acquire(2); System.out.println('子線程1,2執(zhí)行完畢');pool.submit(()->{ System.out.println('Thread3---start'); //信號量加一 semaphore.release(); }); pool.submit(()->{ System.out.println('Thread4---start'); //信號量加一 semaphore.release(); });semaphore.acquire(2); System.out.println('子線程3,4執(zhí)行完畢');//關(guān)閉線程池,正在執(zhí)行的任務繼續(xù)執(zhí)行 pool.shutdown(); }}

詳解Java 信號量Semaphore

二.信號量原理 

看看下面這個圖,可以知道信號量Semaphore還是根據(jù)AQS實現(xiàn)的,內(nèi)部有個Sync工具類操作AQS,還分為公平策略和非公平策略;

詳解Java 信號量Semaphore

構(gòu)造器:

//默認是非公平策略public Semaphore(int permits) { sync = new NonfairSync(permits);}//可以根據(jù)第二個參數(shù)選擇是公平策略還是非公平策略public Semaphore(int permits, boolean fair) { sync = fair ? new FairSync(permits) : new NonfairSync(permits);}

acquire(int permits)方法:

public void acquire(int permits) throws InterruptedException { if (permits < 0) throw new IllegalArgumentException(); sync.acquireSharedInterruptibly(permits);}//AQS中的方法public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); //這里根據(jù)子類是公平策略還是非公平策略 if (tryAcquireShared(arg) < 0) //獲取失敗會進入這里,將線程放入阻塞隊列,然后再嘗試,還是失敗的話就調(diào)用park方法掛起當前線程 doAcquireSharedInterruptibly(arg);}//非公平策略protected int tryAcquireShared(int acquires) { return nonfairTryAcquireShared(acquires);}final int nonfairTryAcquireShared(int acquires) { //一個無限循環(huán),獲取state剩余的信號量,因為每調(diào)用一次release()方法的話,信號量就會加一,這里將 //最新的信號量減去傳進來的參數(shù)比較,比如有兩個線程,其中一個線程已經(jīng)調(diào)用了release方法,然后調(diào)用acquire(2)方法,那么 //這里remaining的值就是-1,返回-1,然后當前線程就會被丟到阻塞隊列中去了;如果另外一個線程也調(diào)用了release方法, //那么此時的remaining==0,所以在這里的if中會調(diào)用CAS將0設(shè)置到state // for (;;) { int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; }}//公平策略//和上面非公平差不多,只不過這里會查看阻塞隊列中當前節(jié)點前面有沒有前驅(qū)節(jié)點,有的話直接返回-1,//就會把當前線程丟到阻塞隊列中阻塞去了,沒有前驅(qū)節(jié)點的話,就跟非公平模式一樣的了protected int tryAcquireShared(int acquires) { for (;;) { if (hasQueuedPredecessors()) return -1; int available = getState(); int remaining = available - acquires; if (remaining < 0 ||compareAndSetState(available, remaining)) return remaining; }}

再看看release(int permits)方法:

//這個方法的作用就是將信號量加一public void release(int permits) { if (permits < 0) throw new IllegalArgumentException(); sync.releaseShared(permits);}//AQS中方法public final boolean releaseShared(int arg) { //tryReleaseShared嘗試釋放資源 if (tryReleaseShared(arg)) { //釋放資源成功就調(diào)用park方法喚醒喚醒AQS隊列中最前面的節(jié)點中的線程 doReleaseShared(); return true; } return false;}protected final boolean tryReleaseShared(int releases) { //一個無限循環(huán),獲取state,然后加上傳進去的參數(shù),如果新的state的值小于舊的state,說明已經(jīng)超過了state的最大值,溢出了 //沒有溢出的話,就用CAS更新state的值 for (;;) { int current = getState(); int next = current + releases; if (next < current) // overflow throw new Error('Maximum permit count exceeded'); if (compareAndSetState(current, next)) return true; }}private void doReleaseShared() { for (;;) { Node h = head; if (h != null && h != tail) { int ws = h.waitStatus; //ws==Node.SIGNAL表示節(jié)點中線程需要被喚醒 if (ws == Node.SIGNAL) {if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; // loop to recheck cases//調(diào)用阻塞隊列中線程的unpark方法喚醒線程unparkSuccessor(h); } //ws == 0表示節(jié)點中線程是初始狀態(tài) else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))continue;// loop on failed CAS }if (h == head) // loop if head changed break; }}

以最上面的例子簡單說一下,其實不是很難,首先線程1和線程2分別去調(diào)用release方法,這個方法里面會將AQS中的state加一,但是在執(zhí)行這個操作之前,主線程肯定會先到acquire(2),在這個方法里面,假如默認使用非公平策略,首先獲取當前的信號量state(state的初始值是0),用當前信號量減去2,如果小于0,那么當前主線程就會丟到AQS隊列中阻塞;

這個時候線程1的release方法執(zhí)行了,于是就把信號量state加一(此時state==1),CAS更新state為一,成功的話,就調(diào)用doReleaseShared()方法喚醒AQS阻塞隊列中最先掛起的線程(這里就是因為調(diào)用acquire方法而阻塞的主線程),主線程喚醒之后又會去獲取最新的信號量,與2比較,發(fā)現(xiàn)還是小于0,于是又會阻塞;

線程2此時的release方法執(zhí)行完成,重復線程一的操作,主線程喚醒之后(此時state==2),又去獲取最新的信號量發(fā)現(xiàn)是2,減去acquire方法的參數(shù)2等于0,于是就用CAS更新state的值,然后acquire方法也就執(zhí)行完畢,主線程繼續(xù)執(zhí)行后面的代碼;

其實信號量還是很有意思的,記得在項目里,有人利用信號量實現(xiàn)了一個故障隔離,什么時候我可以把整理之后的代碼貼出來分享一下,還是很有意思的,就跟springcloud的熔斷機制差不多,場景是:比如你在service的一個方法調(diào)用第三方的接口,你不知道調(diào)不調(diào)得通,而且你不希望每次前端過來都會去調(diào)用,比如當調(diào)用失敗的次數(shù)超過100次,那么五分鐘之后才會再去實際調(diào)用這個第三方服務!這五分鐘內(nèi)前調(diào)用這個服務,就會觸發(fā)我們這個故障隔離的機制,向前端返回一個特定的錯誤碼和錯誤信息!

以上就是詳解Java 信號量Semaphore的詳細內(nèi)容,更多關(guān)于Java 信號量Semaphore的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標簽: Java
相關(guān)文章:
主站蜘蛛池模板: 合肥活动房_安徽活动板房_集成打包箱房厂家-安徽玉强钢结构集成房屋有限公司 | 多功能三相相位伏安表-变压器短路阻抗测试仪-上海妙定电气 | 天津仓库出租网-天津电商仓库-天津云仓一件代发-【博程云仓】 | 宿松新闻网 宿松网|宿松在线|宿松门户|安徽宿松(直管县)|宿松新闻综合网站|宿松官方新闻发布 | 山东商品混凝土搅拌楼-环保型搅拌站-拌合站-分体仓-搅拌机厂家-天宇 | 深圳宣传片制作_产品视频制作_深圳3D动画制作公司_深圳短视频拍摄-深圳市西典映画传媒有限公司 | 山东石英砂过滤器,除氟过滤器「价格低」-淄博胜达水处理 | 智能气瓶柜(大型气瓶储存柜)百科 | 广州云仓代发-昊哥云仓专业电商仓储托管外包代发货服务 | 中式装修设计_全屋定制家具_实木仿古门窗花格厂家-喜迎门 | 有机肥设备生产制造厂家,BB掺混肥搅拌机、复合肥设备生产线,有机肥料全部加工设备多少钱,对辊挤压造粒机,有机肥造粒设备 -- 郑州程翔重工机械有限公司 | 电采暖锅炉_超低温空气源热泵_空气源热水器-鑫鲁禹电锅炉空气能热泵厂家 | 云杂志网-学术期刊-首页| 西安微信朋友圈广告投放_微信朋友圈推广_西安度娘网络科技有限公司 | HV全空气系统_杭州暖通公司—杭州斯培尔冷暖设备有限公司 | 手术室净化厂家_成都实验室装修公司_无尘车间施工单位_洁净室工程建设团队-四川华锐16年行业经验 | 高压无油空压机_无油水润滑空压机_水润滑无油螺杆空压机_无油空压机厂家-科普柯超滤(广东)节能科技有限公司 | 广州中央空调回收,二手中央空调回收,旧空调回收,制冷设备回收,冷气机组回收公司-广州益夫制冷设备回收公司 | 移动厕所租赁|移动卫生间|上海移动厕所租赁-家瑞租赁 | 环氧乙烷灭菌器_压力蒸汽灭菌器_低温等离子过氧化氢灭菌器 _低温蒸汽甲醛灭菌器_清洗工作站_医用干燥柜_灭菌耗材-环氧乙烷灭菌器_脉动真空压力蒸汽灭菌器_低温等离子灭菌设备_河南省三强医疗器械有限责任公司 | 广州企亚 - 数码直喷、白墨印花、源头厂家、透气无手感方案服务商! | 恒温水槽与水浴锅-上海熙浩实业有限公司 | 喷砂机厂家_自动喷砂机生产_新瑞自动化喷砂除锈设备 | 上海三信|ph计|酸度计|电导率仪-艾科仪器 | 打包箱房_集成房屋-山东佳一集成房屋有限公司 | 成都中天自动化控制技术有限公司| 北京网站建设公司_北京网站制作公司_北京网站设计公司-北京爱品特网站建站公司 | 打包钢带,铁皮打包带,烤蓝打包带-高密市金和金属制品厂 | 盐城网络公司_盐城网站优化_盐城网站建设_盐城市启晨网络科技有限公司 | 高压油管,液压接头,液压附件-烟台市正诚液压附件 | 高速龙门架厂家_监控杆_多功能灯杆_信号灯杆_锂电池太阳能路灯-鑫世源照明 | 安全阀_弹簧式安全阀_美标安全阀_工业冷冻安全阀厂家-中国·阿司米阀门有限公司 | 蚂蚁分类信息系统 - PHP同城分类信息系统 - MayiCMS | 自动化生产线-自动化装配线-直流电机自动化生产线-东莞市慧百自动化有限公司 | 湖南长沙商标注册专利申请,长沙公司注册代理记账首选美创! | 右手官网|右手工业设计|外观设计公司|工业设计公司|产品创新设计|医疗产品结构设计|EMC产品结构设计 | 安规_综合测试仪,电器安全性能综合测试仪,低压母线槽安规综合测试仪-青岛合众电子有限公司 | 螺纹三通快插接头-弯通快插接头-宁波舜驰气动科技有限公司 | jrs高清nba(无插件)直播-jrs直播低调看直播-jrs直播nba-jrs直播 上海地磅秤|电子地上衡|防爆地磅_上海地磅秤厂家–越衡称重 | 强效碱性清洗剂-实验室中性清洗剂-食品级高纯氮气发生器-上海润榕科学器材有限公司 | 珠海白蚁防治_珠海灭鼠_珠海杀虫灭鼠_珠海灭蟑螂_珠海酒店消杀_珠海工厂杀虫灭鼠_立净虫控防治服务有限公司 |