單例模式
有些對(duì)象,在一個(gè)程序中應(yīng)該只有唯一 一個(gè)實(shí)例(光靠人保證不靠譜 借助語(yǔ)法來(lái)保證) 就可以使用單例模式
在單例模式下 對(duì)象的實(shí)例化被限制了 只能創(chuàng)建一個(gè) 多了的也創(chuàng)建不了
單例模式分為兩種:餓漢模式和懶漢模式
餓漢模式:
餓急眼了,不吃(創(chuàng)建)不行了,就是在類定義時(shí)就創(chuàng)建一個(gè)實(shí)例
Class Singleton{ private static singleton instance= new singleton(); public static Singleton getInstance(){ return instance; } Private Singleton(){ }(防止new實(shí)例) };
static 靜態(tài)?實(shí)際效果和字面含義沒(méi)啥關(guān)系 實(shí)際的含義是 類屬性/類方法(對(duì)應(yīng)實(shí)例屬性實(shí)例方法)類對(duì)象只能創(chuàng)建一個(gè)
效果是無(wú)論get多少個(gè)實(shí)例都是同一個(gè)實(shí)例 且無(wú)法new實(shí)例
懶漢模式:
ε=(′ο`*)))?啥時(shí)候用啥時(shí)候再創(chuàng)建吧
class Singletonlazy { private static Singgletonlazy instance = null; public static Singletonlazy getInstance(){ If(instance == null){ Instance = new Singletonlazy(); } Return instance; } }
?那么回到多線程,餓漢模式和懶漢模式是線程安全的嗎
餓漢模式get只是多線程讀 沒(méi)問(wèn)題
懶漢模式 第一次調(diào)用get有的地方在讀有的地方在寫 實(shí)例創(chuàng)建好之后就安全了(一個(gè)線程在讀?一個(gè)線程在創(chuàng)建?還沒(méi)創(chuàng)建完?就讀了?就又創(chuàng)建了)
所以我們可以選擇對(duì)它加鎖
synchronized(SingletonLazy.class){ If(instance == null){ Instance = new Singletonlazy(); }}
但是如果這么寫的話,每次的判斷操作也被鎖了,多線程中還是不可以同時(shí)運(yùn)行(一個(gè)判斷另一個(gè)就會(huì)阻塞)所以我們可以
if(instance==null){ synchronized(SingletonLazy.class){ If(instance == null){ Instance = new Singletonlazy(); }}}
在單線程中我們連續(xù)使用兩個(gè)相同的if沒(méi)有意義?但是隔了一個(gè)synchronized就是另一說(shuō)了?
餓漢模式和懶漢模式是一種設(shè)計(jì)思想?不是固定的代碼?我們?cè)倥e一個(gè)例子
1.餓漢 把10g都讀取到內(nèi)存中 讀取完畢之后再允許用戶進(jìn)行查看和修改
2.懶漢 一次只讀一點(diǎn) 隨著用戶翻頁(yè) 繼續(xù)再讀后續(xù)內(nèi)容
生產(chǎn)者消費(fèi)者模型
先來(lái)了解一下?阻塞隊(duì)列
阻塞隊(duì)列和正常隊(duì)列的區(qū)別是啥子呢?就是當(dāng)對(duì)列為空 嘗試出隊(duì)列 就會(huì)阻塞。隊(duì)列為滿時(shí) 嘗試入隊(duì)列也會(huì)阻塞
生產(chǎn)者消費(fèi)者模型就是通過(guò)阻塞隊(duì)列實(shí)現(xiàn)的
我們用包餃子來(lái)舉例?把包出餃子作為最終目標(biāo)(沒(méi)有煮餃子?吃餃子的環(huán)節(jié))
一個(gè)人搟餃子皮 其他人包(因?yàn)閾{面杖數(shù)量有限 所以效率會(huì)遠(yuǎn)高于 每個(gè)人自己搟餃子皮自己包)
生產(chǎn)者消費(fèi)者模型(必須得有蓋簾才算生產(chǎn)者消費(fèi)者模型)
生產(chǎn)者:負(fù)責(zé)搟餃子皮
蓋簾: 阻塞隊(duì)列
消費(fèi)者:負(fù)責(zé)包餃子的人
生產(chǎn)者消費(fèi)者模型的好處是什么
1.可以做到更好的解耦合(高內(nèi)聚就是寫一個(gè)功能的時(shí)候盡量讓這個(gè)功能的代碼集中放置 不要東一塊西一塊)
如果生產(chǎn)者和消費(fèi)者直接交互 耦合性很高 b寄了可能給a也干碎了
如果 生產(chǎn)者->阻塞隊(duì)列->消費(fèi)者 雙方只想著和阻塞隊(duì)列交互就可以了 他倆誰(shuí)寄了都不影響對(duì)方
2.削峰填谷(就跟三峽大壩似得 把雨季的水給旱季) 提高整個(gè)系統(tǒng)的抗風(fēng)險(xiǎn)能力
大規(guī)模用戶同時(shí)訪問(wèn)a a再給b同步 b如果沒(méi)有太強(qiáng)抗壓能力可能就寄了
生產(chǎn)者消費(fèi)者模型
在a和b之間放一個(gè)阻塞隊(duì)列 ?即使a的壓力很大 b仍然按照既定的頻率來(lái)取數(shù)據(jù)
代碼實(shí)現(xiàn)
public static void main(String[] args) { BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(); Thread productor = new Thread(()->{ int n = 0; while(true) { try { System.out.println("生產(chǎn)元素"+n); queue.put(n); n++; Thread.sleep(500);//這行代碼,沒(méi)有實(shí)際意義 是為了讓運(yùn)行結(jié)果更方便觀察而已 } catch (Exception e) { e.printStackTrace(); } } }); productor.start(); Thread customer = new Thread(()->{ while(true) { try { System.out.println("消費(fèi)元素"+queue.take()); Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } } }); customer.start(); }
模擬實(shí)現(xiàn)一個(gè)阻塞隊(duì)列
class myqueue{ int[] queue = new int[100]; int size = 0; int head = 0; int tail = 0; public void put(int a) throws InterruptedException{ synchronized (this) { if(size==queue.length) { this.wait(); } queue[tail] = a; tail++; if(tail==queue.length) { tail = 0; } size++; this.notify(); } } public Integer take() throws InterruptedException{ int ret = 0; synchronized (this) { if(size==0) { this.wait(); } ret = queue[head]; head++; if(head==queue.length) { head=0; } size--; this.notify(); return ret; } } }
不要擔(dān)心這個(gè)notify喚醒的是當(dāng)前功能的wait?因?yàn)槿绻衱ait就做不到這一步
定時(shí)器?
使用方法是這樣的
先實(shí)例化一個(gè)Timer對(duì)象
然后調(diào)用它的schedule方法
schedule有兩個(gè)參數(shù)?第一個(gè)參數(shù)是Timertask對(duì)象?重寫里面的run?就是業(yè)務(wù)代碼
第二個(gè)參數(shù)是時(shí)間?一個(gè)整形?單位ms
public static void main(String[] args) { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { System.out.println("任務(wù)"); } }, 1000); }
你可能會(huì)問(wèn)那我直接用sleep多好
sleep把線程阻塞了?什么都干不了?但是定時(shí)器等待的過(guò)程中還可以干別的
command表示任務(wù)?after表示暫停多久
這個(gè)schedule(時(shí)間表)?這是個(gè)表啊?也就是說(shuō)可以放入多個(gè)元素
模擬實(shí)現(xiàn)
Timer?內(nèi)部要組織很多任務(wù)
還需要有一個(gè)線程?通過(guò)這個(gè)線程來(lái)掃描定時(shí)器內(nèi)部?看看哪個(gè)時(shí)間到了該執(zhí)行了
使用優(yōu)先級(jí)隊(duì)列來(lái)組織這些任務(wù)?使用優(yōu)先級(jí)隊(duì)列可以排序時(shí)間
System.currentTimeMillis()系統(tǒng)當(dāng)前時(shí)間戳\
PriorityQueue<MyTask> queue
這個(gè)隊(duì)列會(huì)被多個(gè)線程同時(shí)訪問(wèn)
schedule可能在多線程中被調(diào)用?每次調(diào)用都要往隊(duì)列里添加元素
內(nèi)部還需要有專門的線程執(zhí)行任務(wù)
涉及到出入?所以不安全
PriorityBlockingQueue用阻塞隊(duì)列
public?void?schedule()
進(jìn)入構(gòu)造就創(chuàng)建一個(gè)線程 循環(huán)不斷嘗試獲取隊(duì)首元素并且判定元素是否可以就緒(大量的產(chǎn)生循環(huán)?空轉(zhuǎn)?沒(méi)有實(shí)際性的任務(wù))
所以?在判斷結(jié)束(不可就緒)后?就讓他等一會(huì)(sleep?不行?即使可以就緒了sleep還在休息?可以用wait?wait可以提前喚醒)(每次插入任務(wù)都喚醒判斷一下)
都統(tǒng)一的時(shí)間戳?如果規(guī)定時(shí)間比當(dāng)前時(shí)間大(沒(méi)到時(shí)間呢)就塞回去?
如果到了?就執(zhí)行
優(yōu)先級(jí)隊(duì)列需要定義優(yōu)先原則?實(shí)現(xiàn)Comparable接口?并重寫compareTo?所以要在被比較元素的類里面寫
這個(gè)return?誰(shuí)減誰(shuí)?可以寫代碼試( 如果第一個(gè)參數(shù)小于第二個(gè)參數(shù),就返回一個(gè)負(fù)數(shù),如果等于就返回0,如果大于就返回一個(gè)正數(shù)。而且要求返回int?還要強(qiáng)轉(zhuǎn))
private Object locker = new Object();
wait?和?notify?都是java.util里面的文件?創(chuàng)建一個(gè)object類就可以
原則上來(lái)講?只要判斷隊(duì)首元素就可以(等待時(shí)間最少)? ?但是要防止新加入的元素比隊(duì)首時(shí)間還小 (所以加了notify?所以說(shuō)用sleep不行)?每次加入新元素都喚醒線程一下?如果這個(gè)新元素的時(shí)間比之前的時(shí)間要早?那就變成按照新的隊(duì)首元素判斷?如果比之前的首元素時(shí)間要晚也無(wú)所謂?接著等唄
?一個(gè)誤區(qū)
這里這個(gè)synchronized?用的是locker作為對(duì)象?但是queue.put沒(méi)有使用locker的資源?是不是就鎖不住了??并不是?它既然鎖住了locker?那么其他線程在運(yùn)行代碼塊的時(shí)候獲取不到locker(已經(jīng)被鎖住)?還是會(huì)阻塞?所以達(dá)成了目的
線程池
進(jìn)程太重量了?創(chuàng)建和銷毀成本較高
線程?就是針對(duì)上述問(wèn)題的優(yōu)化(公用一組系統(tǒng)資源)
但是更頻繁的話?線程也扛不住了
進(jìn)一步優(yōu)化
1.線程池
2.協(xié)程(纖程)?輕量級(jí)線程?
八線程創(chuàng)建好?放在池里?需要使用直接從線程池里面取?用完還回去?就不需要?jiǎng)?chuàng)建和銷毀
?相當(dāng)于十個(gè)線程去運(yùn)行這個(gè)一百個(gè)任務(wù)
模擬實(shí)現(xiàn)
這個(gè)線程是持續(xù)運(yùn)行的?只要submit有東西輸入進(jìn)去了?就會(huì)把它運(yùn)行文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-450488.html
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-450488.html
到了這里,關(guān)于Java-線程安全的四個(gè)經(jīng)典案例和線程池的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!