并发基础知识 — 线程安全性
前段時(shí)間看完了《并發(fā)編程的藝術(shù)》,總感覺(jué)自己對(duì)于并發(fā)缺少一些整體的認(rèn)識(shí)。今天借助《Java并發(fā)編程實(shí)踐》,從一些基本概念開(kāi)始,重新整理一下自己學(xué)過(guò)并發(fā)編程。從并發(fā)基礎(chǔ)開(kāi)始,深入進(jìn)去,系統(tǒng)學(xué)習(xí)一下并發(fā)編程。
編寫(xiě)線程安全的代碼,核心在于要對(duì)狀態(tài)訪問(wèn)操作進(jìn)行管理,特別是對(duì)共享的(Shared)和可變的(Mutable)狀態(tài)的訪問(wèn)。對(duì)象的狀態(tài)是指存儲(chǔ)在狀態(tài)變量(實(shí)例或靜態(tài)域)中的數(shù)據(jù)。對(duì)象的狀態(tài)還可能包括其他依賴(lài)對(duì)象的域。(Map.Entry)
一個(gè)對(duì)象是否需要時(shí)線程安全的,取決于該對(duì)象是否被多線程訪問(wèn)。這指的是程序中訪問(wèn)對(duì)象的方式,而不是對(duì)象要實(shí)現(xiàn)的功能。要使得對(duì)象是線程安全的,要采用同步機(jī)制來(lái)協(xié)同對(duì)對(duì)象可變狀態(tài)的訪問(wèn)。Java常用的同步機(jī)制是Synchronized,還包括?volatile類(lèi)型的變量,顯示鎖以及原子變量。
線程安全的程序是否完全由線程安全的類(lèi)構(gòu)成?答案是否定的,完全由線程安全的類(lèi)構(gòu)成的程序并不一定是線程安全的,線程安全類(lèi)中也可以包含非線程安全的類(lèi)。只有當(dāng)類(lèi)中僅包含自己的狀態(tài)時(shí),線程安全類(lèi)才有意義!
什么是線程安全性?
當(dāng)多個(gè)線程訪問(wèn)某個(gè)類(lèi)時(shí),不管運(yùn)行時(shí)環(huán)境采用何種調(diào)度方式或者這些線程將如何交替執(zhí)行,并且在主調(diào)代碼中不需要任何額外的同步或協(xié)同,這個(gè)類(lèi)都能表現(xiàn)出正確的行為,那么這個(gè)類(lèi)就是線程安全的。
正確性:某個(gè)類(lèi)的行為與其規(guī)范相一致。(我理解的規(guī)范就是我們?cè)诰帉?xiě)類(lèi)時(shí),能預(yù)知的狀態(tài)結(jié)果)
原子性
競(jìng)態(tài)條件(Race Condition):某個(gè)計(jì)算的正確性取決于多個(gè)線程的交替執(zhí)行的時(shí)序。(線程的時(shí)序不同,產(chǎn)生的結(jié)果可能會(huì)不同)
“先檢查后執(zhí)行”,即通過(guò)一個(gè)可能失效的觀測(cè)結(jié)果來(lái)決定下一步的操作。
首先觀察到某個(gè)條件為真,然后開(kāi)始執(zhí)行相關(guān)的程序,但是在多線程的運(yùn)行環(huán)境中,條件判斷的結(jié)果以及開(kāi)始執(zhí)行程序中間,觀察結(jié)果可能變得無(wú)效(另外一個(gè)線程在此期間執(zhí)行了相關(guān)的動(dòng)作),從而導(dǎo)致無(wú)效。常見(jiàn)的就是(Lazy Singleton)
LazySingleton“讀取-修改-寫(xiě)入”,基于對(duì)象之前的狀態(tài)來(lái)定義對(duì)象狀態(tài)的轉(zhuǎn)換。即使是volatile修飾的變量,在多線程的環(huán)境里面進(jìn)行自增操作,同樣會(huì)發(fā)生競(jìng)態(tài)條件,所以volatile不能保證絕對(duì)的線程安全(360面試問(wèn)題)。
引用書(shū)中定義:假定有兩個(gè)操作A和B,如果從執(zhí)行A的線程來(lái)看,當(dāng)另一個(gè)線程執(zhí)行B時(shí),要么將B完全執(zhí)行完,要么完全不執(zhí)行B,那么A和B對(duì)彼此來(lái)說(shuō)是原子的。原子操作是指:對(duì)于訪問(wèn)同一個(gè)狀態(tài)的所有操作(包括該操作本身)來(lái)說(shuō),這個(gè)操作是一個(gè)以原子方式執(zhí)行的操作。
加鎖機(jī)制
在線程安全的定義中,多個(gè)線程間的操作無(wú)論采用何種執(zhí)行時(shí)序或交替方式,都要保證不變性條件不被破壞。當(dāng)不變性條件中涉及多個(gè)變量時(shí),各個(gè)變量之間并不是互相獨(dú)立的,一個(gè)變量發(fā)生變化會(huì)對(duì)其他變量的值產(chǎn)生約束。因此,一個(gè)變量發(fā)生改變,在同一個(gè)原子操作里面,其他相關(guān)變量也要更新。
內(nèi)置鎖:同步代碼塊(Synchronized Block)包括兩部分:一個(gè)作為鎖的對(duì)象引用,一個(gè)作為由這個(gè)鎖保護(hù)的代碼塊。關(guān)鍵字Synchronized修飾方法就是一種同步代碼塊,鎖就是方法調(diào)用所在的對(duì)象,靜態(tài)的Synchronized方法以Class對(duì)象作為鎖。內(nèi)置鎖或監(jiān)視鎖就是以對(duì)象作為實(shí)現(xiàn)同步的鎖。
Java內(nèi)置鎖,進(jìn)入的唯一途徑是執(zhí)行進(jìn)入由鎖保護(hù)的同步代碼塊或方法。它相當(dāng)于一種互斥鎖。
重入鎖:當(dāng)一個(gè)持有鎖的線程再次請(qǐng)求進(jìn)入自己持有的鎖時(shí),該請(qǐng)求會(huì)成功。"重入"意味著獲取鎖的操作的粒度是“線程”,而不是“調(diào)用”。重入的一種實(shí)現(xiàn)方式,為每個(gè)鎖關(guān)聯(lián)一個(gè)計(jì)數(shù)器和線程持有者。
用鎖來(lái)保護(hù)狀態(tài)
由于鎖能使其保護(hù)的代碼路徑以串行形式訪問(wèn),因此可以通過(guò)鎖來(lái)構(gòu)造一些協(xié)議以實(shí)現(xiàn)對(duì)共享狀態(tài)的獨(dú)占訪問(wèn)。
對(duì)象的內(nèi)置鎖與其狀態(tài)之間沒(méi)有內(nèi)在的聯(lián)系,雖然大多數(shù)類(lèi)都將內(nèi)置鎖用做一種有效的加鎖機(jī)制,但對(duì)象的域并不一定要通過(guò)內(nèi)置鎖來(lái)保護(hù)。
對(duì)于每個(gè)包含多個(gè)變量的不變性條件,其中涉及的所有變量都要使用同一個(gè)鎖來(lái)保護(hù)/同步。
https://www.cnblogs.com/tcming/p/6711506.html
總結(jié)
以上是生活随笔為你收集整理的并发基础知识 — 线程安全性的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 支付宝网商银行有什么用
- 下一篇: 2018住房公积金贷款额度 个人需要满