日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
如果你這樣回答“什么是線程安全”,面試官都會(huì)對(duì)你刮目相看

不是線程的安全

創(chuàng)新互聯(lián)公司是一家專業(yè)提供陸豐企業(yè)網(wǎng)站建設(shè),專注與成都做網(wǎng)站、成都網(wǎng)站建設(shè)、H5響應(yīng)式網(wǎng)站、小程序制作等業(yè)務(wù)。10年已為陸豐眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站設(shè)計(jì)公司優(yōu)惠進(jìn)行中。

面試官問(wèn):“什么是線程安全”,如果你不能很好的回答,那就請(qǐng)往下看吧。

論語(yǔ)中有句話叫“學(xué)而優(yōu)則仕”,相信很多人都覺(jué)得是“學(xué)習(xí)好了可以做官”。然而,這樣理解卻是錯(cuò)的。切記望文生義。

同理,“線程安全”也不是指線程的安全,而是指內(nèi)存的安全。為什么如此說(shuō)呢?這和操作系統(tǒng)有關(guān)。

在每個(gè)進(jìn)程的內(nèi)存空間中都會(huì)有一塊特殊的公共區(qū)域,通常稱為堆(內(nèi)存)。進(jìn)程內(nèi)的所有線程都可以訪問(wèn)到該區(qū)域,這就是造成問(wèn)題的潛在原因。

假設(shè)某個(gè)線程把數(shù)據(jù)處理到一半,覺(jué)得很累,就去休息了一會(huì),回來(lái)準(zhǔn)備接著處理,卻發(fā)現(xiàn)數(shù)據(jù)已經(jīng)被修改了,不是自己離開(kāi)時(shí)的樣子了??赡鼙黄渌€程修改了。

比如把你住的小區(qū)看作一個(gè)進(jìn)程,小區(qū)里的道路/綠化等就屬于公共區(qū)域。你拿1萬(wàn)塊錢往地上一扔,就回家睡覺(jué)去了。睡醒后你打算去把它撿回來(lái),發(fā)現(xiàn)錢已經(jīng)不見(jiàn)了??赡鼙粍e人拿走了。

因?yàn)楣矃^(qū)域人來(lái)人往,你放的東西在沒(méi)有看管措施時(shí),一定是不安全的。內(nèi)存中的情況亦然如此。

所以線程安全指的是,在堆內(nèi)存中的數(shù)據(jù)由于可以被任何線程訪問(wèn)到,在沒(méi)有限制的情況下存在被意外修改的風(fēng)險(xiǎn)。

即堆內(nèi)存空間在沒(méi)有保護(hù)機(jī)制的情況下,對(duì)多線程來(lái)說(shuō)是不安全的地方,因?yàn)槟惴胚M(jìn)去的數(shù)據(jù),可能被別的線程“破壞”。

那我們?cè)撛趺崔k呢?解決問(wèn)題的過(guò)程其實(shí)就是一個(gè)取舍的過(guò)程,不同的解決方案有不同的側(cè)重點(diǎn)。

私有的東西就不該讓別人知道

現(xiàn)實(shí)中很多人都會(huì)把1萬(wàn)塊錢藏著掖著,不讓無(wú)關(guān)的人知道,所以根本不可能扔到大馬路上。因?yàn)檫@錢是你的私有物品。

在程序中也是這樣的,所以操作系統(tǒng)會(huì)為每個(gè)線程分配屬于它自己的內(nèi)存空間,通常稱為棧內(nèi)存,其它線程無(wú)權(quán)訪問(wèn)。這也是由操作系統(tǒng)保障的。

如果一些數(shù)據(jù)只有某個(gè)線程會(huì)使用,其它線程不能操作也不需要操作,這些數(shù)據(jù)就可以放入線程的棧內(nèi)存中。較為常見(jiàn)的就是局部變量。

 
 
 
  1. double avgScore(double[] scores) {
  2.  double sum = 0;
  3.  for (double score : scores) {
  4.  sum += score;
  5.  }
  6.  int count = scores.length;
  7.  double avg = sum / count;
  8.  return avg;
  9. }

這里的變量sum,count,avg都是局部變量,它們都會(huì)被分配在線程棧內(nèi)存中。

假如現(xiàn)在A線程來(lái)執(zhí)行這個(gè)方法,這些變量會(huì)在A的棧內(nèi)存分配。與此同時(shí),B線程也來(lái)執(zhí)行這個(gè)方法,這些變量也會(huì)在B的棧內(nèi)存中分配。

也就是說(shuō)這些局部變量會(huì)在每個(gè)線程的棧內(nèi)存中都分配一份。由于線程的棧內(nèi)存只能自己訪問(wèn),所以棧內(nèi)存中的變量只屬于自己,其它線程根本就不知道。

就像每個(gè)人的家只屬于自己,其他人不能進(jìn)來(lái)。所以你把1萬(wàn)塊錢放到家里,其他人是不會(huì)知道的。且一般還會(huì)放到某個(gè)房間里,而不是仍在客廳的桌子上。

所以把自己的東西放到自己的私人地盤,是安全的,因?yàn)槠渌藷o(wú)法知道。而且越隱私的地方越好。

大家不要搶,人人有份

相信聰明的你已經(jīng)發(fā)現(xiàn),上面的解決方案是基于“位置”的。因?yàn)槟惴艝|西的“位置”只有你自己知道(或能到達(dá)),所以東西是安全的,因此這份安全是由“位置”來(lái)保障的。

在程序里就對(duì)應(yīng)于方法的局部變量。局部變量之所以是安全的,就是因?yàn)槎x它的“位置”是在方法里。這樣一來(lái)安全是達(dá)到了,但是它的使用范圍也就被限制在這個(gè)方法里了,其它方法想用也不用了啦。

現(xiàn)實(shí)中往往會(huì)有一個(gè)變量需要多個(gè)方法都能夠使用的情況,此時(shí)定義這個(gè)變量的“位置”就不能在方法里面了,而應(yīng)該在方法外面。即從(方法的)局部變量變?yōu)?類的)成員變量,其實(shí)就是“位置”發(fā)生了變化。

那么按照主流編程語(yǔ)言的規(guī)定,類的成員變量不能再分配在線程的棧內(nèi)存中,而應(yīng)該分配在公共的堆內(nèi)存中。其實(shí)也就是變量在內(nèi)存中的“位置”發(fā)生了變化,由一個(gè)私有區(qū)域來(lái)到了公共區(qū)域。因此潛在的安全風(fēng)險(xiǎn)也隨之而來(lái)。

那怎么保證在公共區(qū)域的東西安全呢?答案就是,大家不要搶,人人有份。設(shè)想你在街頭免費(fèi)發(fā)放礦泉水,來(lái)了1萬(wàn)人,你卻只有1千瓶水,結(jié)果可想而知,一擁而上,場(chǎng)面失守。但如果你有10萬(wàn)瓶水,大家一看,水多著呢,不用著急,一個(gè)個(gè)排著隊(duì)來(lái),因?yàn)榭隙〞?huì)領(lǐng)到。

東西多了,自然就不值錢了,從另一個(gè)角度來(lái)說(shuō),也就安全了。大街上的共享單車,現(xiàn)在都很安全,因?yàn)樘嗔?,到處都是,都長(zhǎng)得一樣,所以連搞破壞的人都放棄了。因此要讓一個(gè)東西安全,就瘋狂的copy它吧。

回到程序里,要讓公共區(qū)域堆內(nèi)存中的數(shù)據(jù)對(duì)于每個(gè)線程都是安全的,那就每個(gè)線程都拷貝它一份,每個(gè)線程只處理自己的這一份拷貝而不去影響別的線程的,這不就安全了嘛。相信你已經(jīng)猜到了,我要表達(dá)的就是ThreadLocal類了。

 
 
 
  1. class StudentAssistant {
  2.  ThreadLocal realName = new ThreadLocal<>();
  3.  ThreadLocal totalScore = new ThreadLocal<>();
  4.  String determineDegree() {
  5.  double score = totalScore.get();
  6.  if (score >= 90) {
  7.  return "A";
  8.  }
  9.  if (score >= 80) {
  10.  return "B";
  11.  }
  12.  if (score >= 70) {
  13.  return "C";
  14.  }
  15.  if (score >= 60) {
  16.  return "D";
  17.  }
  18.  return "E";
  19.  }
  20.  double determineOptionalcourseScore() {
  21.  double score = totalScore.get();
  22.  if (score >= 90) {
  23.  return 10;
  24.  }
  25.  if (score >= 80) {
  26.  return 20;
  27.  }
  28.  if (score >= 70) {
  29.  return 30;
  30.  }
  31.  if (score >= 60) {
  32.  return 40;
  33.  }
  34.  return 60;
  35.  }
  36. }

這個(gè)學(xué)生助手類有兩個(gè)成員變量,realName和totalScore,都是ThreadLocal類型的。每個(gè)線程在運(yùn)行時(shí)都會(huì)拷貝一份存儲(chǔ)到自己的本地。

A線程運(yùn)行的是“張三”和“90”,那么這兩個(gè)數(shù)據(jù)“張三”和“90”是存儲(chǔ)到A線程對(duì)象(Thread類的實(shí)例對(duì)象)的成員變量里去了。假設(shè)此時(shí)B線程也在運(yùn)行,是“李四”和“85”,那么“李四”和“85”這兩個(gè)數(shù)據(jù)是存儲(chǔ)到了B線程對(duì)象(Thread類的實(shí)例對(duì)象)的成員變量里去了。

線程類(Thread)有一個(gè)成員變量,類似于Map類型的,專門用于存儲(chǔ)ThreadLocal類型的數(shù)據(jù)。從邏輯從屬關(guān)系來(lái)講,這些ThreadLocal數(shù)據(jù)是屬于Thread類的成員變量級(jí)別的。從所在“位置”的角度來(lái)講,這些ThreadLocal數(shù)據(jù)是分配在公共區(qū)域的堆內(nèi)存中的。

說(shuō)的直白一些,就是把堆內(nèi)存中的一個(gè)數(shù)據(jù)復(fù)制N份,每個(gè)線程認(rèn)領(lǐng)1份,同時(shí)規(guī)定好,每個(gè)線程只能玩自己的那份,不準(zhǔn)影響別人的。

需要說(shuō)明的是這N份數(shù)據(jù)都還是存儲(chǔ)在公共區(qū)域堆內(nèi)存里的,經(jīng)常聽(tīng)到的“線程本地”,是從邏輯從屬關(guān)系上來(lái)講的,這些數(shù)據(jù)和線程一一對(duì)應(yīng),仿佛成了線程自己“領(lǐng)地”的東西了。其實(shí)從數(shù)據(jù)所在“位置”的角度來(lái)講,它們都位于公共的堆內(nèi)存中,只不過(guò)被線程認(rèn)領(lǐng)了而已。這一點(diǎn)我要特地強(qiáng)調(diào)一下。

其實(shí)就像大街上的共享單車。原來(lái)只有1輛,大家搶著騎,老出問(wèn)題?,F(xiàn)在從這1輛復(fù)制出N輛,每人1輛,各騎各的,問(wèn)題得解。共享單車就是數(shù)據(jù),你就是線程。騎行期間,這輛單車從邏輯上來(lái)講是屬于你的,從所在位置上來(lái)講還是在大街上這個(gè)公共區(qū)域的,因?yàn)槟惆l(fā)現(xiàn)每個(gè)小區(qū)大門口都貼著“共享單車,禁止入門”。哈哈哈哈。

共享單車是不是和ThreadLocal很像呀。再重申一遍,ThreadLocal就是,把一個(gè)數(shù)據(jù)復(fù)制N份,每個(gè)線程認(rèn)領(lǐng)一份,各玩各的,互不影響。

只能看,不能摸

放在公共區(qū)域的東西,只是存在潛在的安全風(fēng)險(xiǎn),并不是說(shuō)一定就不安全。有些東西雖然也在公共區(qū)域放著,但也是十分安全的。比如你在大街上放一個(gè)上百噸的石頭雕像,就非常安全,因?yàn)榇蠹叶寂粍?dòng)它。

再比如你去旅游時(shí),經(jīng)常發(fā)現(xiàn)一些珍貴的東西,會(huì)被用鐵柵欄圍起來(lái),上面掛一個(gè)牌子,寫著“只能看,不能摸”。當(dāng)然可以國(guó)際化一點(diǎn),“only look,don't touch”。這也是很安全的,因?yàn)楣饪磶籽凼遣豢赡芸磯牡摹?/p>

回到程序里,這種情況就屬于,只能讀取,不能修改。其實(shí)就是常量或只讀變量,它們對(duì)于多線程是安全的,想改也改不了。

 
 
 
  1. class StudentAssistant {
  2.  final double passScore = 60;
  3. }

比如把及格分?jǐn)?shù)設(shè)定為60分,在前面加上一個(gè)final,這樣所有線程都動(dòng)不了它了。這就很安全了。

小節(jié)一下:以上三種解決方案,其實(shí)都是在“?;ㄕ小?。

***種,找個(gè)只有自己知道的地方藏起來(lái),當(dāng)然安全了。

第二種,每人復(fù)制1份,各玩各的,互不影響,當(dāng)然也安全了。

第三種,更狠了,直接規(guī)定,只能讀取,禁止修改,當(dāng)然也安全了。

是不是都在“避重就輕”呀。如果這三種方法都解決不了,該怎么辦呢?Don't worry,just continue reading。

沒(méi)有規(guī)則,那就先入為主

前面給出的三種方案,有點(diǎn)“理想化”了。現(xiàn)實(shí)中的情況其實(shí)是非?;靵y嘈雜的,沒(méi)有規(guī)則的。

比如在中午高峰期你去飯店吃飯,進(jìn)門后發(fā)現(xiàn)只剩一個(gè)空桌子了,你心想先去點(diǎn)餐吧,回來(lái)就坐這里吧。當(dāng)你點(diǎn)完餐回來(lái)后,發(fā)現(xiàn)已經(jīng)被別人捷足先登了。

因?yàn)樽雷邮菍儆诠矃^(qū)域的物品,任何人都可以坐,那就只能誰(shuí)先搶到誰(shuí)坐。雖然你在人群中曾多看了它一眼,但它并不會(huì)記住你容顏。

解決方法就不用我說(shuō)了吧,讓一個(gè)人在那兒看著座位,其它人去點(diǎn)餐。這樣當(dāng)別人再來(lái)的時(shí)候,你就可以理直氣壯的說(shuō),“不好意思,這個(gè)座位,我,已經(jīng)占了”。

我再次相信聰明的你已經(jīng)猜到了我要說(shuō)的東西了,沒(méi)錯(cuò),就是(互斥)鎖。

回到程序里,如果公共區(qū)域(堆內(nèi)存)的數(shù)據(jù),要被多個(gè)線程操作時(shí),為了確保數(shù)據(jù)的安全(或一致)性,需要在數(shù)據(jù)旁邊放一把鎖,要想操作數(shù)據(jù),先獲取鎖再說(shuō)吧。

假設(shè)一個(gè)線程來(lái)到數(shù)據(jù)跟前一看,發(fā)現(xiàn)鎖是空閑的,沒(méi)有人持有。于是它就拿到了這把鎖,然后開(kāi)始操作數(shù)據(jù),干了一會(huì)活,累了,就去休息了。

這時(shí),又來(lái)了一個(gè)線程,發(fā)現(xiàn)鎖被別人持有著,按照規(guī)定,它不能操作數(shù)據(jù),因?yàn)樗鼰o(wú)法得到這把鎖。當(dāng)然,它可以選擇等待,或放棄,轉(zhuǎn)而去干別的。

***個(gè)線程之所以敢大膽的去睡覺(jué),就是因?yàn)樗掷锬弥i呢,其它線程是不可能操作數(shù)據(jù)的。當(dāng)它回來(lái)后繼續(xù)把數(shù)據(jù)操作完,就可以把鎖給釋放了。鎖再次回到空閑狀態(tài),其它線程就可以來(lái)?yè)屵@把鎖了。還是誰(shuí)先搶到鎖誰(shuí)操作數(shù)據(jù)。

 
 
 
  1. class ClassAssistant {
  2.  double totalScore = 60;
  3.  final Lock lock = new Lock();
  4.  void addScore(double score) {
  5.  lock.obtain();
  6.  totalScore += score;
  7.  lock.release();
  8.  }
  9.  void subScore(double score) {
  10.  lock.obtain();
  11.  totalScore -= score;
  12.  lock.release();
  13.  }
  14. }

假定一個(gè)班級(jí)的初始分?jǐn)?shù)是60分,這個(gè)班級(jí)抽出10名學(xué)生來(lái)同時(shí)參加10個(gè)不同的答題節(jié)目,每個(gè)學(xué)生答對(duì)一次為班級(jí)加上5分,答錯(cuò)一次減去5分。因?yàn)?0個(gè)學(xué)生一起進(jìn)行,所以這一定是一個(gè)并發(fā)情形。

因此加分和減分這兩個(gè)方法被并發(fā)的調(diào)用,它們共同操作總分?jǐn)?shù)。為了保證數(shù)據(jù)的一致性,需要在每次操作前先獲取鎖,操作完成后再釋放鎖。

相信世界充滿愛(ài),即使被傷害

再回到一開(kāi)始的例子,假如你往地上扔1萬(wàn)塊錢,是不是一定會(huì)丟呢?這要看情況了,如果是在人來(lái)人往的都市,可以說(shuō)肯定會(huì)丟的。如果你跑到無(wú)人區(qū)扔地上,可以說(shuō)肯定不會(huì)丟。

可以看到,都是把東西無(wú)保護(hù)的放到公共區(qū)域里,結(jié)果卻相差很大。這說(shuō)明安全問(wèn)題還和公共區(qū)域的環(huán)境狀況有關(guān)系。

比如我把數(shù)據(jù)放到公共區(qū)域的堆內(nèi)存中,但是始終都只會(huì)有1個(gè)線程,也就是單線程模型,那這數(shù)據(jù)肯定是安全的。

再者說(shuō),2個(gè)線程操作同一個(gè)數(shù)據(jù)和200個(gè)線程操作同一個(gè)數(shù)據(jù),這個(gè)數(shù)據(jù)的安全概率是完全不一樣的。肯定線程越多數(shù)據(jù)不安全的概率越大,線程越少數(shù)據(jù)不安全的概率越小。取個(gè)極限情況,那就是只有1個(gè)線程,那不安全概率就是0,也就是安全的。

可能你又猜到了我想表達(dá)的內(nèi)容了,沒(méi)錯(cuò),就是CAS??赡艽蠹矣X(jué)得既然鎖可以解決問(wèn)題,那就用鎖得了,為啥又冒出了個(gè)CAS呢?

那是因?yàn)殒i的獲取和釋放是要花費(fèi)一定代價(jià)的,如果在線程數(shù)目特別少的時(shí)候,可能根本就不會(huì)有別的線程來(lái)操作數(shù)據(jù),此時(shí)你還要獲取鎖和釋放鎖,可以說(shuō)是一種浪費(fèi)。

針對(duì)這種“地廣人稀”的情況,專門提出了一種方法,叫CAS(Compare And Swap)。就是在并發(fā)很小的情況下,數(shù)據(jù)被意外修改的概率很低,但是又存在這種可能性,此時(shí)就用CAS。

假如一個(gè)線程操作數(shù)據(jù),干了一半活,累了,想要去休息。(貌似今天的線程體質(zhì)都不太好)。于是它記錄下當(dāng)前數(shù)據(jù)的狀態(tài)(就是數(shù)據(jù)的值),回家睡覺(jué)了。

醒來(lái)后打算繼續(xù)接著干活,但是又擔(dān)心數(shù)據(jù)可能被修改了,于是就把睡覺(jué)前保存的數(shù)據(jù)狀態(tài)拿出來(lái)和現(xiàn)在的數(shù)據(jù)狀態(tài)比較一下,如果一樣,說(shuō)明自己在睡覺(jué)期間,數(shù)據(jù)沒(méi)有被人動(dòng)過(guò)(當(dāng)然也有可能是先被改成了其它,然后又改回來(lái)了,這就是ABA問(wèn)題了),那就接著繼續(xù)干。如果不一樣,說(shuō)明數(shù)據(jù)已經(jīng)被修改了,那之前做的那些操作其實(shí)都白瞎了,就干脆放棄,從頭再重新開(kāi)始處理一遍。

所以CAS這種方式適用于并發(fā)量不高的情況,也就是數(shù)據(jù)被意外修改的可能性較小的情況。如果并發(fā)量很高的話,你的數(shù)據(jù)一定會(huì)被修改,每次都要放棄,然后從頭再來(lái),這樣反而花費(fèi)的代價(jià)更大了,還不如直接加鎖呢。

這里再解釋下ABA問(wèn)題,假如你睡覺(jué)前數(shù)據(jù)是5,醒來(lái)后數(shù)據(jù)還是5,并不能肯定數(shù)據(jù)沒(méi)有被修改過(guò)??赡軘?shù)據(jù)先被修改成8然后又改回到5,只是你不知道罷了。對(duì)于這個(gè)問(wèn)題,其實(shí)也很好解決,再加一個(gè)版本號(hào)字段就行了,并規(guī)定只要修改數(shù)據(jù),必須使版本號(hào)加1。

這樣你睡覺(jué)前數(shù)據(jù)是5版本號(hào)是0,醒來(lái)后數(shù)據(jù)是5版本號(hào)是0,表明數(shù)據(jù)沒(méi)有被修改。如果數(shù)據(jù)是5版本號(hào)是2,表明數(shù)據(jù)被改動(dòng)了2次,先改為其它,然后又改回到5。

我再次相信聰明的你已經(jīng)發(fā)現(xiàn)了,這里的CAS其實(shí)就是樂(lè)觀鎖,上一種方案里的獲取鎖和釋放鎖其實(shí)就是悲觀鎖。樂(lè)觀鎖持樂(lè)觀態(tài)度,就是假設(shè)我的數(shù)據(jù)不會(huì)被意外修改,如果修改了,就放棄,從頭再來(lái)。悲觀鎖持悲觀態(tài)度,就是假設(shè)我的數(shù)據(jù)一定會(huì)被意外修改,那干脆直接加鎖得了。


分享題目:如果你這樣回答“什么是線程安全”,面試官都會(huì)對(duì)你刮目相看
文章路徑:http://m.5511xx.com/article/dhsjjhe.html