您好,登錄后才能下訂單哦!
今天小編給大家分享一下Java并發編程之線程安全性怎么實現的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
當多個線程訪問某個類時,不管運行時環境采用何種調用方式或者這些線程將如何交替執行,并且在主調代碼中不需要任何額外的同步或協同,這個類都能表現出正確的行為,那么就稱這個類是線程安全的。
無狀態的對象一定是線程安全的,比如:Servlet
。
由于不恰當的執行時序而出現不正確的結果的情況,就是競爭條件。
“先檢查后執行”操作,即通過一個可能實效的觀測結果來決定下一步的動作。比如:延遲初始化。
if(instance == null) { instance = new SomeObject(); }
“讀取-修改-寫入”的操作,其結果狀態依賴于之前的狀態。如:遞增運算。
long count = 0; count++;
原子操作是指,對于訪問同一個狀態的所有操作(包括此操作本身)來說,這個操作是以一個原子方式執行(不可分割)的操作。
為了確保線程安全性,包含了一組必須以原子方式執行的操作,稱為復合操作。
遞增運算可以使用一個現有的線程安全類,確保線程安全性。如:
AtomicLong count = new AtomicLong(0); count.incrementAndGet();
當類只有一個狀態變量時,可以通過線程安全的狀態變量來維護類的線程安全性。但如果類有更多的狀態時,就不能只添加更多線程安全的狀態變量了。要保持狀態的一致性,就需要在單個原子操作中更新所以相關的狀態變量。
Java提供一種內置鎖:同步代碼塊,它包括:一個作為鎖的對象引用、一個作為由這個鎖保護的代碼塊。
以關鍵字synchronized
來修飾的方法就是一種橫跨整個方法體的同步代碼塊,其中該同步代碼塊的鎖就是方法調用所在的對象。靜態的synchronized
方法以Class對象作為鎖。
線程在進入同步代碼塊之前會自動獲得鎖,在退出同步代碼塊是自動釋放鎖。最多只有一個線程能持有這種鎖,因此同步代碼會以原子方式執行。
內置鎖是可重入的,意味著獲取鎖的操作的粒度是線程,不是調用。當某個線程試圖獲得一個已經由它自己持有的鎖時,這個請求也會成功。
重入進一步提升了加鎖行為的封裝性,簡化了面向對象并發代碼的開發。
public class Widget { public synchronized void doSomething() { //...... } } public class LoggingWidget extends Widget { public synchronized void doSomething() { //...... super.doSomething();//假如沒有可重入的鎖,該語句將產生死鎖。 } }
對于可能被多個線程同時訪問的可變狀態變量,在訪問它時都需要持有同一個鎖,在這種情況下,稱狀態變量是由這個鎖保護的。
粗粒度地使用鎖,雖然確保了線程安全性,但可能造成性能問題和活躍度問題,如:
@ThreadSafe public class SynchronizedFactorizer implements Servlet { @GuardedBy("this") private BigInteger lastNumber; @GuardedBy("this") private BigInteger[] lastFactors; public synchronized void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); if (i.equals(lastNumber)) encodeIntoResponse(resp, lastFactors); else { BigInteger[] factors = factor(i);//因數分解計算 lastNumber = i; lastFactors = factors;//存放上一次計算結果 encodeIntoResponse(resp, factors); } } }
可以通過縮小同步代碼塊,既確保servlet的并發型,又維護線程安全性。不要把本應是原子操作拆分到多個同步代碼塊中,盡量將不影響共享狀態且執行時間較長的操作從同步代碼中分離出來。如:
public class CachedFactorizer implements Servlet { @GuardedBy("this") private BigInteger lastNumber; @GuardedBy("this") private BigInteger[] lastFactors; public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); BigInteger[] factors = null; synchronized (this) { if (i.equals(lastNumber)) { factors = lastFactors.clone(); } } if (factors == null) { factors = factor(i); synchronized (this) { lastNumber = i; lastFactors = factors.clone(); } } encodeIntoResponse(resp, factors); } }
以上就是“Java并發編程之線程安全性怎么實現”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。