????01-多線程(概述)
? ? ? ? 接下來惭嚣,我們來說一說Java中特有的一個知識技術:多線程瞳脓。
? ? ? ? 在說線程這個概念之前呢,需要說一個更顯而易見的概念:進程。
? ? ? ? 何為進程呢形庭?進程就是正在進行中的程序铅辞。
? ? ? ? 進程可以同時開啟,其實就是cpu在對它們執(zhí)行萨醒。
? ? ? ? 不過斟珊,雖然它們看起來像是同時在執(zhí)行,其實验靡,cpu在某一時刻只能執(zhí)行某一個程序倍宾,它只是在做著超快的切換,咻咻咻咻~才導致我們看到的是各個程序在同時執(zhí)行胜嗓。
? ? ? ? 再講個迅雷的例子。是不是有時候會有多條下載請求呀钩乍?一個進程辞州,里面可能會有多條執(zhí)行路徑。什么意思呢寥粹?在我們進行迅雷下載的時候呢变过,大家都知道迅雷可以有多條下載路徑,比如100M的文件涝涤,迅雷可能會分成五個部分媚狰,一個部分一個部分的同時發(fā)送請求到服務端去下載數(shù)據(jù)。我們想一想阔拳,如果我們下載是用一個路徑崭孤,就是先下載第一個數(shù)據(jù),然后一個一個下載糊肠,一直下載到第100個數(shù)據(jù)辨宠。可是如果是五條路徑下載货裹,五個人同時在搬東西嗤形,是不是就比一個人快多啦?而且這五個人都是在這一個進程中哦弧圆,這其中的每一個人赋兵,就叫做線程。線程是進程中的內(nèi)容搔预,每一個應用程序霹期,里面都至少有一個線程。
? ? ? ? 線程有一個特點斯撮,它是程序中的控制單元经伙,或者叫執(zhí)行路徑。
? ? ? ? 總結(jié)一下:
? ? ? ? 進程:是一個正在執(zhí)行中的程序。
? ? ? ? ? ? ? ? ?每一個進程執(zhí)行都有一個執(zhí)行順序帕膜,該順序是一個執(zhí)行路徑枣氧,或者叫一個控制單元。
? ? ? ? 每個程序執(zhí)行垮刹,都需要啟動內(nèi)存空間达吞,而進程其實就是用來標識內(nèi)存空間的,它用來封裝里面的那些控制單元荒典。
? ? ? ? 線程:就是進程中的一個獨立的控制單元酪劫。
? ? ? ? ? ? ? ? ? 線程在控制著進程的執(zhí)行。
? ? ? ? ? ? ? ? ? 一個進程中至少有一個線程寺董。
? ? ? ? 以Java為例覆糟,編譯運行的時候,它有兩個進程:編譯進程和運行進程遮咖。
? ? ? ? 我們重點說一下運行進程滩字。
? ? ? ? Java虛擬機啟動的時候會有一個進程java.exe。該進程中至少有一個線程來負責Java程序的執(zhí)行御吞,而且這個線程運行的代碼存在于main方法中麦箍。該線程稱之為主線程。
? ? ? ? 其實陶珠,很多書里面都在說挟裂,這個程序在啟動執(zhí)行的時候是單線程程序,因為還沒有其他線程存在呢揍诽。這樣說理解也是對的诀蓉。
? ? ? ? 擴展:
? ? ? ? 但是,如果你深究一下虛擬機的話寝姿,其實不是單線程交排。虛擬機啟動的時候,它就是多線程饵筑。為什么呢埃篓?另外的線程是誰呢?
? ? ? ? 試想一下根资,(這個只作為了解喔架专,因為說這個程序是單線程也沒問題噠)程序在運行的時候,是不是要建立對象玄帕,調(diào)用方法部脚,這個時候是主線程在幫我們做這件事情,堆里面會產(chǎn)生很多對象裤纹,如果其中某一個對象不被使用了委刘,它就會被垃圾回收機制回收了丧没。主線程還在繼續(xù)執(zhí)行著其他對象中的操作,而這個對象就被干掉了锡移,是不是在同時進行吶呕童?是滴!
? ? ? ? 這個時候就產(chǎn)生了一個問題淆珊,主線程在繼續(xù)調(diào)用方法的同時夺饲,另外的不被使用的對象就被垃圾回收機制回收了。所以這個時候虛擬機至少有兩個線程施符,一個是主線程往声,一個是垃圾回收的線程。
? ? ? ? 多線程存在的意義是什么呢戳吝?
? ? ? ? 多線程的出現(xiàn)呢浩销,可以讓我們程序中的部分產(chǎn)生同時運行的效果。而且在下載東西的時候骨坑,多線程下載還可以幫我們提高效率撼嗓。
????02-多線程(創(chuàng)建線程-繼承Thread類)
? ? ? ? 那么,如何在我們的程序中自定一個控制單元欢唾,或者說,自定一個線程粉捻?
? ? ? ? Java中已經(jīng)為我們提供好了對這類事物的對象體現(xiàn)礁遣。
? ? ? ? 通過對API的查找,我們發(fā)現(xiàn)肩刃,java的核心包java.lang包中就有一個對象叫做Thread祟霍,它就是程序中的執(zhí)行線程,就是用于描述控制單元這類事物的對象盈包。
? ? ? ? 我們發(fā)現(xiàn)創(chuàng)建新執(zhí)行線程有兩種方法:
? ? ? ? 連范例都有啦沸呐,我們自己來寫~
? ? ? ? 創(chuàng)建線程的第一種方式:繼承Thread類。? ? ? ?
? ? ? ? 第一步蒙具,繼承Thread類:
? ? ? ? 接下來我們看看Thread中的run方法是怎樣的~
? ? ? ? 第二部俐填,重寫run方法:
? ? ? ? 繼承完之后揣非,我們接下來就要創(chuàng)建它的對象啦。注意哦呼渣,建立好一個對象,其實就是創(chuàng)建好一個線程寞埠。
? ? ? ? 創(chuàng)建好對象之后就要調(diào)用run方法啦屁置。
? ? ? ? 我們看到范例中有一個start方法:
? ? ? ? 查一下start方法是干什么的~
? ? ? ? OK,寫好啦:
? ??????
? ? ? ? 現(xiàn)在編譯運行:
? ? ? ? demo run成功打印了仁连。
? ???????總結(jié)一下蓝角,?創(chuàng)建線程的第一種方式:繼承Thread類的步驟為:
? ??????1,定義類繼承Thread。
? ? ? ? 2使鹅,復寫Thread類中的run方法揪阶。
? ? ? ? 3,調(diào)用線程的start方法并徘,該方法有兩個作用:啟動線程遣钳,調(diào)用run方法。
? ? ? ? 冷靜下來思考一下麦乞,但是這跟我們以前調(diào)用方法的方式有什么區(qū)別呢蕴茴?怎么看不出來呀?
? ? ? ? 接下來我們讓它多運行一會兒姐直,寫了一個循環(huán)~
? ? ? ? 讓hello world也參與一下~
? ? ? ? 編譯運行:
????????我們分析一下它的執(zhí)行路徑:?
? ? ? ? 所以我們在打印結(jié)果的時候是交替打印的倦淀。
? ? ? ? 為什么是交替的呢?main線程和d線程它們倆不是同時執(zhí)行嗎声畏?
? ? ? ? 跟你講哦撞叽,真實情況下是不可能的。
? ? ? ? windows本身是一個多任務操作系統(tǒng)插龄,看上去它確實在同時執(zhí)行愿棋,其實真正的情況是,cpu在某一時刻下均牢,只能執(zhí)行一個程序糠雨。為什么看起來是同時執(zhí)行的呢?因為cpu在這些程序之間做著快速的切換徘跪,切換的速度是相當快的甘邀,快到你根本感覺不出來它在切換,所以你覺得它在同時執(zhí)行垮庐。
? ? ? ? 而進程中真正在執(zhí)行的是線程松邪,所以cpu在切換的是每一個進程中的線程。而一個進程中有多個線程的話哨查,cpu也要做切換呢逗抑。(這也是機器中程序開的越多越慢的原因)
? ? ? ? 我們現(xiàn)在就不說多進程啦,我們就說其中一個進程好啦解恰。
? ? ? ? 在這個例子中锋八,這一個進程中,就已經(jīng)有多個線程啦护盈。cpu執(zhí)行main一會兒挟纱,執(zhí)行d一會兒。那么這種情況腐宋,我們形象的把它稱之為紊服,多個線程在搶劫cpu資源檀轨。這只是一種形象的說法,其實是cpu說了算欺嗤。只是說“搶”更形象一點兒~
? ? ? ? #Java小劇場
? ? ? ? cpu:執(zhí)行main一會兒参萄,執(zhí)行main一會兒,好了好了煎饼,再執(zhí)行d一會兒讹挎,再執(zhí)行d一會兒,再執(zhí)行d一會兒吆玖,誒筒溃,好像都沒顧到main了,那我再執(zhí)行main一會兒好了沾乘。怜奖。。
? ? ? ? #
? ? ? ? 早期有一些病毒就是通過消耗cpu資源讓電腦死機的翅阵。它就是一直在搶用cpu資源歪玲,從而達到讓別人搶不著的效果。
? ? ? ? 再回到我們的例子中掷匠。
? ? ? ? 到這里main線程就結(jié)束啦滥崩,那這個進程會結(jié)束嗎?不會讹语。因為d線程還沒有結(jié)束夭委,只要d線程還在,這個進程就存在募强。
? ? ? ? 現(xiàn)在有了雙核、多核處理器崇摄,就可以實現(xiàn)同時運行了擎值,這個cpu處理這個,那個cpu處理那個逐抑。不過具體是怎么切換的鸠儿,我們現(xiàn)在暫時控制不了,可以學一下多核編程玩一下~
? ? ? ? 雙核以后誰是瓶頸厕氨?內(nèi)存进每。
? ? ? ? #Java小劇場
? ? ? ? 小楠老師在做一個一對三的高考沖刺班,她同時帶三個孩子命斧,一會給這個輔導田晚,一會給那個輔導,還算忙得過來国葬。這個時候又有一個人報名了贤徒,小楠老師忙不過來了芹壕,于是就又請了一位老師,這位新老師來帶新報名的這個孩子接奈。這樣踢涌,小楠老師和新老師可以同時帶四個孩子。
? ? ? ? 小楠老師的補習班空間不大序宦,四個孩子就剛好坐滿啦睁壁。其實新老師也可以再帶兩個孩子呢,這樣小楠老師和新老師就可以同時帶六個孩子互捌,達到最大的效率潘明。可是因為補習班太小疫剃,不能容納更多孩子钉疫,小楠老師和新老師還是只能帶四個孩子。
? ? ? ? #
? ? ? ? 現(xiàn)在雙核之后巢价,還有四核牲阁、八核,聽起來好膩害哦壤躲。但是可能會出現(xiàn)cpu想處理數(shù)據(jù)但沒數(shù)據(jù)處理的情況城菊。(沒有地方裝數(shù)據(jù)呀)
? ? ? ? 再回到剛剛的例子中。我們又多運行了幾次碉克,發(fā)現(xiàn)每次運行打印的順序都不同凌唬。
? ? ? ? 因為多個線程都在獲取cpu的執(zhí)行權(quán)。cpu執(zhí)行到誰漏麦,誰就運行客税。
? ? ? ? 明確一點,在某一個時刻撕贞,只能有一個程序在運行更耻。(多核除外)
? ? ? ? cpu在做著快速的切換,以達到看上去是同時運行的效果捏膨。
? ? ? ? 我們可以形象的把多線程的運行行為看做在互相搶奪cpu的執(zhí)行權(quán)秧均。
? ? ? ? 這就是多線程的一個特性:隨機性。誰搶到誰執(zhí)行号涯,至于執(zhí)行多長時間目胡,cpu說的算。
? ? ? ? 當然链快,也會出現(xiàn)這樣的情況誉己,hello world執(zhí)行完了,才執(zhí)行demo run久又,或者demo run執(zhí)行完了巫延,才想起來執(zhí)行hello world效五。這種情況是cpu切換得有點慢。哈哈~遲鈍的cpu~
? ? ? ? 再當然炉峰,在沒有被特意控制的情況下畏妖,不會出現(xiàn)cpu把一個全部執(zhí)行完再執(zhí)行另一個的情況。 因為cpu它在優(yōu)化資源疼阔,它得去快速的做著切換戒劫,才能實現(xiàn)同時運行的效果,否則會出現(xiàn)一個程序在執(zhí)行婆廊,另一個程序執(zhí)行不了迅细。
????04-多線程(線程練習)
? ? ? ? 練習:創(chuàng)建兩個線程,和主線程交替運行淘邻。
? ? ? ? 運行結(jié)果:
? ? ? ? 這樣寫會怎樣呢:
? ? ? ? 還是只有一個主線程在執(zhí)行茵典,另外兩個線程沒有開啟。
? ? ? ? 主線程先打印one run宾舅,再打印two run统阿,最后打印main。
? ? ? ? 這里我們的循環(huán)只有60次筹我,所以主線程運行一段程序的時候扶平,其他程序等待時間不會太長,但是如果是6000蔬蕊、60000次呢结澄?這個其他程序的等待時間就很長啦。
? ? ? ? 所以岸夯,建立多個線程麻献,多個程序就可以“同時”運行~
????05-多線程(線程運行狀態(tài))
? ? ? ? 線程的四種狀態(tài):
????06-多線程(獲取線程對象以及名稱)
? ? ? ? 我們來看看關于線程名稱的方法,有setName:
? ? ? ? getName:
? ? ? ? 我們用getName方法來獲取一下線程名稱猜扮。
? ? ? ? 依然用之前的那個例子~
? ? ? ? 編譯運行:
? ? ? ? 我們發(fā)現(xiàn)有Thread-1和Thread-0兩個名字赎瑰。
? ? ? ? 原來線程都有自己默認的名稱。
? ? ? ? Thread-編號 該編號從0開始破镰。
? ? ? ? 我們也可以改變線程的名稱,用setName方法就好啦压储。
? ? ? ? 不過查一下Thread的構(gòu)造函數(shù)鲜漩,發(fā)現(xiàn)線程初始化的時候就可以有名稱啦!
? ? ? ? 我們調(diào)用一下父類的構(gòu)造方法:
? ? ? ? 現(xiàn)在在創(chuàng)建它們的時候就給它們起一下名字:
? ? ? ? 編譯運行:
? ? ? ? 自定義線程名稱成功啦~
? ? ? ? 另外集惋,Thread類還為我們提供了一個方法:currentThread()孕似。
? ? ? ? 這個方法是靜態(tài)的,說明這個對象沒有訪問到對象的特有數(shù)據(jù)刮刑,用類名訪問就可以啦喉祭。
? ? ? ? 我們使用這個方法养渴,來獲得當前線程的名稱:
? ? ? ? 獲取成功~(運行截圖略,就和this.getName()運行結(jié)果一樣)
? ? ? ? 既然兩種方法運行結(jié)果一樣泛烙,它們有什么不同呢理卑?
? ? ? ? 試一下:
? ? ? ? 運行:
? ? ? ? 運行結(jié)果是true哦。所以它倆是一樣噠蔽氨。
? ? ? ? 那用this調(diào)用就行了呀藐唠,多簡單,用currentThread那么長鹉究,那么麻煩宇立。
? ? ? ? 不是的。this調(diào)用的方式并不通用自赔,只有在類的對象調(diào)用類中方法時才可以用則中方式調(diào)用妈嘹。而currentThread是標準通用方法,無論誰調(diào)用都可以用~
? ? ? ? 我們來復習一下:
? ? ? ? static Thread currentThread():獲取當前線程對象绍妨。
? ? ? ? getName():獲取線程名稱润脸。
? ? ? ? 設置線程名稱:setName()或者構(gòu)造函數(shù)。
? ? ? ? 還有一個小問題:
? ? ? ? Thread-0和Thread-1“同時”運行的時候痘绎,用的x是同一個嗎津函?
? ? ? ? 不是的。
? ? ? ? Thread-0建立的時候孤页,內(nèi)存中會為它分配一塊內(nèi)存空間尔苦,其中有一塊叫x;Thread-1建立的時候行施,內(nèi)存也會為它分配另一塊內(nèi)存空間允坚,其中也有一塊叫x。多個線程“同時”運行的時候蛾号,注意局部變量是每個內(nèi)存空間中都有一份哦稠项。
????07-多線程(售票的例子)
? ? ? ? 接下來,我們用一個事例來對第二種方法進行闡述鲜结。
? ? ? ? 需求:簡單的賣票程序展运。
? ? ? ? 多個窗口同時賣票。
? ? ? ? 現(xiàn)在4個窗口同時賣票:
? ? ? ? 運行一下精刷,發(fā)現(xiàn)分不清都是哪個窗口賣的:
? ? ? ? 打印一下線程的名字:
? ? ? ? 運行拗胜,我們發(fā)現(xiàn)了一個問題:
? ? ? ? 1、2怒允、3埂软、4號窗口都賣了1號票。一節(jié)車廂就100個座纫事,可是現(xiàn)在賣出了400個座勘畔。
? ? ? ? 問題在于所灸,創(chuàng)建一個對象,里面就有100張票炫七。
? ? ? ? 我們的解決方式:讓4個對象共享100張票爬立。
? ? ? ? 該用靜態(tài)啦!
? ? ? ? 運行:
? ? ? ? OK啦诉字。沒有重復賣票的啦懦尝。
? ? ? ? 但是,我們一般是不是不定義靜態(tài)呀壤圃?因為它的生命周期太長啦陵霉!
? ? ? ? 那怎么解決呢?
? ? ? ? 用一個對象來賣100張票:
? ? ? ? 運行:
? ? ? ? 是不是也賣完啦~
? ? ? ? 但是發(fā)現(xiàn)這些亂七八糟什么東西呀:
? ? ? ? 也就是說伍绳,線程已經(jīng)開啟了踊挠,并且調(diào)用start函數(shù),從開啟狀態(tài)進入了運行狀態(tài)冲杀。這個時候又調(diào)用了start函數(shù)效床,又進入運行狀態(tài),這就是無效的呀权谁。
? ? ? ? 該怎么解決呢剩檀?
? ? ? ? 快接著看下去~
????08-多線程(創(chuàng)建線程-實現(xiàn)Runnable接口)
? ? ? ? 解決這個問題,我們就需要引入第二種創(chuàng)建線程的方式了旺芽。
? ? ? ? 我們來看一下Runnable接口:
? ? ? ? 這個接口里面非常的爽呀沪猴,就一個方法:
? ? ? ? 我們來跟著示例代碼來寫~emmm...不太懂耶。
? ? ? ? 那慢慢一步一步分析著來~先讓Ticket類實現(xiàn)Runnable接口:
? ? ? ? 主函數(shù)中:
? ? ? ? 我們現(xiàn)在需要想辦法讓線程調(diào)用Ticket中的run采章,需要讓Ticket中的run和Thread創(chuàng)建的對象有關系运嗜!
? ? ? ? 我們需要在創(chuàng)建線程對象時就明確要運行什么代碼。
? ? ? ? Thread有一個構(gòu)造方法悯舟,比較特殊:
? ? ? ? 它可以接收Runnable接口類型的對象担租。
? ? ? ? 所以這就是我們要實現(xiàn)Runnable接口的原因,因為Thread類認識這個Runnable接口抵怎。
? ? ? ? 所以奋救,我們在new Thread方法的同時,就可以指定run方法所屬的對象反惕。
? ? ? ? 編譯運行:
? ? ? ? 搞定菠镇!
? ? ? ? 這就是創(chuàng)建線程的第二種方式:實現(xiàn)Runnable接口。
? ? ? ? 步驟:
? ? ? ? 1承璃,定義類實現(xiàn)Runnable接口。
? ? ? ? 2蚌本,覆蓋Runnable接口中的run方法盔粹。
? ? ? ? ? ? ? ? 將線程要運行的代碼存放在該run方法中隘梨。
? ? ? ? 3,通過Thread類建立線程對象舷嗡。
? ? ? ? 4轴猎,將Runnable接口的子類對象作為實際參數(shù)傳遞給Thread類的構(gòu)造函數(shù)。
? ? ? ? ? ? ? ? 為什么要將Runnable接口的子類對象傳遞給Thread的構(gòu)造函數(shù)进萄?
? ? ? ? ? ? ? ? 因為捻脖,自定義的run方法所屬的對象是Runnable接口的子類對象。
? ? ? ? ? ? ? ? 所以要讓線程去指定對象的run方法中鼠。就必須明確該run方法所屬的對象可婶。
? ? ? ? 5,調(diào)用Thread類的start方法開啟線程援雇,并調(diào)用Runnable接口子類的run方法矛渴。
? ? ? ? 那么,這兩種創(chuàng)建線程的方式惫搏,也就是實現(xiàn)方式和繼承方式有什么區(qū)別呢具温?
? ? ? ? 我們先講一下Runnable接口的由來:
? ? ? ? 左邊的Student類,沒有父類筐赔,它里面的run方法需要被多線程執(zhí)行铣猩,所以就繼承了Thread類。
? ? ? ? 可是茴丰,Student在演變的過程中达皿,抽取出來了一個父類Person,這個時候Student類中的run方法需要被多線程執(zhí)行较沪,但是因為它已經(jīng)有父類了鳞绕,而Java又不支持多繼承,所以無法再繼承Thread類尸曼。
? ? ? ? 這時们何,Java提供了一個Runnable接口來解決這個問題,“你可以不叫我爸爸控轿,我也可以幫你執(zhí)行代碼冤竹,只要你符合我的規(guī)則就行”,將這個功能抽取出來封裝到了Runnable接口中茬射。
? ? ? ? 這就是接口的由來鹦蠕。
? ? ? ? 因此,實現(xiàn)方式的好處:避免了單繼承的局限性在抛,把它作為了一種功能的擴展封裝在了接口中钟病。在定義線程時,建議使用實現(xiàn)方式。
? ? ? ? 而且這個構(gòu)造方法使用了多態(tài):
? ? ? ? 因為日后會出現(xiàn)什么樣的接口子類是無法預知的肠阱,所以它用了多態(tài)的形式票唆,你只要符合規(guī)則,它都可以用屹徘!你只要符合PCI的規(guī)則走趋,后期粗現(xiàn)什么樣的版本,它都可以幫你運行噪伊!
? ? ? ? 我們發(fā)現(xiàn)簿煌,Thread類本身也實現(xiàn)了Runnable接口:
? ? ? ? Runnable接口的定義,其實就是在確立線程要運行代碼所存放的位置鉴吹。
? ? ? ? 兩種創(chuàng)建線程的方式還有一個區(qū)別:
? ? ? ? 繼承Thread:線程代碼存放在Thread子類的run方法中姨伟。
? ? ? ? 實現(xiàn)Runnable:線程代碼存放在接口子類的run方法中。
? ? ? ? 它們代碼的存放位置不一樣拙寡。
? ? ? ? 當然授滓,如果你的類沒有父類,用第一種方式也是完全可以的肆糕。
????09-多線程(多線程的安全問題)
? ? ? ? 現(xiàn)在只剩最后一張票了般堆,然后4個窗口都拿到了執(zhí)行資格,在等待執(zhí)行權(quán)诚啃,最后執(zhí)行權(quán)比如說給了0號淮摔,0號賣出后執(zhí)行權(quán)比如給了1號,這個時候1再繼續(xù)執(zhí)行票數(shù)就為負了始赎,這顯然不符合常理和橙。我們的程序有bug啦!?
? ? ? ? 我們現(xiàn)在模擬一下這個過程造垛,讓我們真實的看到這個現(xiàn)象魔招。
? ? ? ? 怎么做呢?
? ? ? ? 那怎么讓它睡一下呢五辽?
? ? ? ? 看一下sleep方法:
? ? ? ? 我們看一下這個方法的特點:
? ? ? ? 它是靜態(tài)的办斑,沒有訪問到對象的特有數(shù)據(jù),并且它拋出了異常杆逗。
? ? ? ? 話不多說乡翅,寫起來:
? ? ? ? 因為Ticket實現(xiàn)了Runnable接口,而這個接口并未拋出異常罪郊,所以Ticket也不能拋出異常蠕蚜,所以只能try哦。
? ? ? ? 運行一下:
? ? ? ? 剛剛那個問題就出現(xiàn)啦悔橄。
? ? ? ? 通過分析靶累,發(fā)現(xiàn)腺毫,打印出0,-1挣柬,-2等錯票拴曲。
? ? ? ? 多線程的運行出現(xiàn)了安全問題。
? ? ? ? 安全問題最可怕辣凛忿!
? ? ? ? 多線程中一定要小心安全問題,一旦產(chǎn)生就非常要命竞川。
? ? ? ? 問題的原因:
? ? ? ? 當多條語句在操作同一個線程共享數(shù)據(jù)時店溢,一個線程對多條語句只執(zhí)行了一部分,還沒有執(zhí)行完委乌,另一個線程參與進來執(zhí)行床牧,導致了共享數(shù)據(jù)的錯誤。
? ? ? ? 解決辦法:
? ? ? ? 對多條操作共享數(shù)據(jù)的語句遭贸,只能讓一個線程都執(zhí)行完戈咳,在執(zhí)行過程中,其他過程不可以參與執(zhí)行壕吹。
? ? ? ? Java對于多線程的安全問題提供了專業(yè)的解決方式著蛙。
? ? ? ? 就是同步代碼塊。
? ? ? ? synchronized(對象)
? ? ? ? {
? ? ? ? ? ? ? ? 需要被同步的代碼
? ? ? ? }
? ? ? ? 寫起來~
? ? ? ? 運行看一下效果:
? ? ? ? OK啦耳贬!
? ??10-多線程(多線程同步代碼塊)
? ? ? ? 在上節(jié)課中踏堡,這里有個對象:
? ? ? ? 這個對象如同鎖,持有鎖的線程可以在同步中執(zhí)行咒劲。
? ? ? ? 沒有持有鎖的線程顷蟆,即使獲得了cpu的執(zhí)行權(quán),也進不去腐魂,因為沒有獲取鎖帐偎。
? ? ? ? 火車上的衛(wèi)生間就是一個經(jīng)典的的例子。
? ? ? ? 同步的前提:
? ? ? ? 1蛔屹,必須要有兩個或者兩個以上的線程才需要同步削樊。(就你一個人用這個衛(wèi)生間,就不需要鎖門啦)
? ? ? ? 2判导,必須是多個線程使用同一個鎖才需要同步嫉父。(多個人使用的是同一個衛(wèi)生間)
? ? ? ? 必須保證同步中只能有一個線程在運行。
? ? ? ? 同步的好處:
? ? ? ? 解決了多線程的安全問題眼刃。
? ? ? ? 不過它也有弊端:雖然解決了問題绕辖,但是執(zhí)行的時候每次都要判斷這個鎖,所以較為消耗資源擂红。(越安全越麻煩)