JAVA-初步认识-第十三章-多线程(验证同步函数的锁)
一.
至于同步函數(shù)用的是哪個(gè)鎖,我們可以驗(yàn)證一下,借助原先賣票的例子
?對(duì)于程序中的num,從100改為400,DOS的結(jié)果顯示的始終都是0線程,票號(hào)最小都是1。
票號(hào)是沒(méi)有問(wèn)題的,因?yàn)橥搅恕?/span>
有人針對(duì)只出現(xiàn)0線程,說(shuō)是票數(shù)太少,0線程都給操作完了。即使改成四萬(wàn)張票,也是0線程操作。
正常來(lái)說(shuō),四個(gè)線程0~3,誰(shuí)搶到誰(shuí)就運(yùn)行。問(wèn)題出現(xiàn)在哪兒?
程序中run函數(shù)是public synchronized void run(),沒(méi)有搞清楚什么時(shí)候用同步,什么時(shí)候不用同步。
我們知道該同步的是if整個(gè)語(yǔ)句,
現(xiàn)在我們是從run方法就開(kāi)始了同步,也就是說(shuō)在run語(yǔ)句的上面,同時(shí)來(lái)了四個(gè)線程,一個(gè)線程在run方法里沒(méi)執(zhí)行完所有的語(yǔ)句,其他線程就無(wú)法進(jìn)來(lái)。而run里面就包含著循環(huán)售票,也就是說(shuō)一個(gè)線程進(jìn)到run方法里,售完了票才出去。
看下圖,在if中0線程處于sleep狀態(tài)時(shí),1~3號(hào)線程得到了執(zhí)行權(quán),但是進(jìn)不來(lái)。
而且,0線程出不去了,因?yàn)閣hile中的判斷始終是正確的。(是不是說(shuō),即使循環(huán)執(zhí)行完了,0線程還呆在里面,因?yàn)閣hile始終是正確的?)
出現(xiàn)這樣的原因就是不需要將run方法整體同步,只需要將if代碼塊同步,怎么解決呢?
單獨(dú)地將if語(yǔ)句定義成函數(shù),將該函數(shù)同步,接著調(diào)用即可。
DOS結(jié)果顯示,四個(gè)線程都出來(lái)了。(這里show方法可以直接調(diào)用,不用創(chuàng)建對(duì)象啥的么?)
二. 驗(yàn)證同步函數(shù)的鎖
基于上面的例子,我們來(lái)驗(yàn)證同步函數(shù)的鎖是哪一個(gè)?
現(xiàn)在為了方便起見(jiàn),將四個(gè)線程減少為兩個(gè)線程。兩個(gè)線程運(yùn)行的動(dòng)作是一樣的,都在賣票。一個(gè)是在同步代碼塊里賣票,一個(gè)是在同步函數(shù)中賣票。(同步函數(shù)和同步代碼塊兩者不是一樣么?)
如果這兩個(gè)線程用的是同一個(gè)鎖的話,就不會(huì)出現(xiàn)安全隱患。0線程在同步函數(shù)里,1線程在同步代碼塊里,如果它倆用的是同一個(gè)鎖,那說(shuō)明0線程在運(yùn)行同步函數(shù)的時(shí)候,1線程不能運(yùn)行同步代碼塊的,(這是不是說(shuō)明同步代碼塊和同步函數(shù)都是靠鎖來(lái)操作的原理?)
想要在run方法里,既有同步代碼塊又能調(diào)用同步函數(shù),這需要什么動(dòng)作?這叫做線程的切換。為真的時(shí)候,運(yùn)行同步代碼塊:為假的時(shí)候,運(yùn)行同步函數(shù),這需要一個(gè)boolean型變量。
(這里的while語(yǔ)句始終感覺(jué)沒(méi)什么用,為什么要一直保留著?)
?為真的時(shí)候,走同步代碼塊,為假的時(shí)候,走同步代碼塊。
兩個(gè)線程都進(jìn)到run方法里面去了,它們都有自己的run方法,判斷的變量也是同一變量flag。
DOS結(jié)果線程,1線程和0線程都有,但是都在function里面執(zhí)行。按理說(shuō),0線程為真應(yīng)該在同步代碼塊中執(zhí)行,怎么跑到同步函數(shù)中執(zhí)行了?理由:主線程開(kāi)啟以后,創(chuàng)建對(duì)象,創(chuàng)建兩個(gè)線程。開(kāi)啟線程1以后,它還持有cpu的執(zhí)行權(quán),所以瞬間,將t1.start(),t.flag=false,t2.start()這三句話全部都搞定了。一搞定后,這個(gè)flag就變?yōu)榧倭?#xff0c;主線程搞完假后,這兩個(gè)線程在啟動(dòng)的時(shí)候都是flag=false,因此兩個(gè)線程在執(zhí)行的時(shí)候,執(zhí)行的都是同步函數(shù)。(為什么主線程能執(zhí)行這么多語(yǔ)句?怎么判別主線程和0,1線程是執(zhí)行的哪些語(yǔ)句?)
有人說(shuō)這里沒(méi)切換啊。可以做切換。
主線程開(kāi)啟了0線程以后,把它置為假之前,可以讓主線程停一下。也就是調(diào)用sleep方法,讓主線程睡一下,這樣0線程就掌握?qǐng)?zhí)行權(quán)了。
睡了10毫秒。
DOS結(jié)果顯示如上。如果兩個(gè)線程同步了,就不會(huì)出現(xiàn)負(fù)的情況,如果沒(méi)同步就有可能出現(xiàn)安全問(wèn)題。
怎么輸出兩個(gè)49?操作線程的代碼有四句,obj兩句,function兩句。你判斷完了,我也判斷完了,你沒(méi)輸出,我也沒(méi)輸出。我49輸出,我也49輸出。
但是,我現(xiàn)在想說(shuō)的是0號(hào)票,打印0號(hào)票肯定是不對(duì)的。加上同步的居然不安全。為什么?
首先這里面應(yīng)該有多線程,同步代碼塊里面是一個(gè)線程,同步函數(shù)里用的又是另一個(gè)線程,它們用的 不是一個(gè)鎖。如果用的是用一個(gè)鎖,代表著同一個(gè)鎖里有多個(gè)線程,意味著每次只能有一個(gè)線程進(jìn)來(lái)。這里可以說(shuō)明的一點(diǎn)的是,同步函數(shù)用的鎖肯定不是obj,那用的是什么鎖?同步函數(shù)僅僅是函數(shù)上帶了同步性,同步本身不帶鎖。同步代碼塊后面是單獨(dú)指定鎖,synchronized是關(guān)鍵字,本身并不帶鎖。
應(yīng)該是函數(shù)帶的鎖,函數(shù)有對(duì)象,函數(shù)被調(diào)用的時(shí)候,必須是對(duì)象來(lái)調(diào)用。函數(shù)是被哪個(gè)對(duì)象調(diào)用呢?
函數(shù)是被this調(diào)用,函數(shù)都有自己所屬的this。函數(shù)被哪個(gè)對(duì)象調(diào)用?我哪兒知道,我肯定函數(shù)是被對(duì)象調(diào)用,憑什么去操作對(duì)應(yīng)的數(shù)據(jù)啊?因?yàn)槌钟衪his。
這個(gè)show被誰(shuí)調(diào)用?被run方法調(diào)用,至于run方法被誰(shuí)調(diào)用,換句話說(shuō)run方法所屬于哪個(gè)對(duì)象。當(dāng)然屬于t。run方法不是封裝線程任務(wù)么?不是把線程任務(wù)所屬對(duì)象t創(chuàng)建出來(lái)了么,那么就是t在調(diào)用run方法對(duì)象。show怎么獲取的t,當(dāng)然this嘛。
一般方法調(diào)用一般方法,直接寫個(gè)this,即this,show();
run方法也屬于this,直接把this寫入同步代碼塊中,哪個(gè)對(duì)象調(diào)用這個(gè)run方法,它就代表哪個(gè)對(duì)象。這個(gè)this所指的地址和下面的t地址是一致的
DOS結(jié)果顯示,兩者是同一個(gè)對(duì)象。
現(xiàn)在將添加的兩個(gè)輸出語(yǔ)句注釋掉,
繼續(xù)編譯運(yùn)行
0線程和1線程將票賣完了,也不存在0號(hào)票。由此可以驗(yàn)證同步函數(shù)使用的鎖是this。
上面的程序可以不用寫那么多,現(xiàn)在為什么寫呢?是為了講解同步函數(shù)使用的鎖是this。
同步函數(shù)可以是同步代碼塊的簡(jiǎn)寫,一簡(jiǎn)寫就有前提,有弊端。如果同步代碼塊里的鎖不是this,那就不能用同步函數(shù)了。
同步函數(shù)雖然簡(jiǎn)化,但是鎖是唯一的。
?
?
?
?
?
?
?
?
?
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/wsw-bk/p/8033847.html
總結(jié)
以上是生活随笔為你收集整理的JAVA-初步认识-第十三章-多线程(验证同步函数的锁)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 前端开发-DOM
- 下一篇: 文件2. 文件重命名