【併發編程】什麼是線程安全性?

線程安全性

線程安全性定義:
當多個線程訪問某個類時,無論運行時環境採用何種調度方式或者這些進程將如何交替執行,而且在主調代碼中不須要任何額外的同步或協同,這個類都能表現出正確的行爲,那麼就稱這個類是線程安全的。html

三個基本概念

在併發編程中,咱們一般會遇到如下三個問題:原子性問題,可見性問題,有序性問題。咱們先看具體看一下這三個概念:java

原子性

原子性:是指一個操做或多個操做要麼所有執行,且執行的過程不會被任何因素打斷,要麼就都不執行。編程

原子性提供了互斥訪問,同一時刻只能有一個線程來對它進行操做。緩存

可見性

可見性:當一個線程修改了線程共享變量的值,其它線程可以當即得知這個修改。安全

一個線程對主內存的修改能夠及時的被其餘線程觀察到。Java內存模型是經過在變量修改後將新值同步回主內存,在變量讀取前從主內存刷新變量值這種依賴主內存做爲傳遞媒介的方法來實現可見性的,不管是普通變量仍是volatile變量都是如此。多線程

有序性

有序性:即程序執行的順序按照代碼的前後順序執行。併發

Java內存模型中的程序自然有序性能夠總結爲一句話:若是在本線程內觀察,全部操做都是有序的;若是在一個線程中觀察另外一個線程,全部操做都是無序的。前半句是指「線程內表現爲串行語義」,後半句是指「指令重排序」現象和「工做內存主主內存同步延遲」現象。app

有序性的語意有幾層,高併發

  1. 最多見的就是保證多線程運行的串行順序
  2. 防止重排序引發的問題
  3. 程序運行的前後順序。比方JMM定義的一些Happens-before規則

指令重排序不會影響單個線程的執行,可是會影響到線程併發執行的正確性。性能

總結

Java內存模型是圍繞着在併發過程當中如何處理原子性、可見性和有序性這3個特徵來創建的。
也就是說,要想併發程序正確地執行,必需要保證原子性、可見性以及有序性。只要有一個沒有被保證,就有可能會致使程序運行不正確。

在Java虛擬機規範中試圖定義一種Java內存模型(Java Memory Model,JMM)來屏蔽各個硬件平臺和操做系統的內存訪問差別,以實現讓Java程序在各類平臺下都能達到一致的內存訪問效果。那麼Java內存模型規定了哪些東西呢,它定義了程序中變量的訪問規則,往大一點說是定義了程序執行的次序。注意,爲了得到較好的執行性能,Java內存模型並無限制執行引擎使用處理器的寄存器或者高速緩存來提高指令執行速度,也沒有限制編譯器對指令進行重排序。也就是說,在java內存模型中,也會存在緩存一致性問題和指令重排序的問題。

Java內存模型規定全部的變量都是存在主存當中(相似於前面說的物理內存),每一個線程都有本身的工做內存(相似於前面的高速緩存)。線程對變量的全部操做都必須在工做內存中進行,而不能直接對主存進行操做。而且每一個線程不能訪問其餘線程的工做內存。(詳細見:【JVM】Java內存模型

那麼Java語言 自己對 原子性、可見性以及有序性提供了哪些保證呢?

一、原子性

在Java中,對基本數據類型的變量的讀取和賦值操做是原子性操做,即這些操做是不可被中斷的,要麼執行,要麼不執行。也就是說,只有簡單的讀取、賦值(並且必須是將數字賦值給某個變量,變量之間的相互賦值不是原子操做)纔是原子操做。
Java內存模型只保證了基本讀取和賦值是原子性操做,若是要實現更大範圍操做的原子性,能夠經過synchronized和Lock來實現。因爲synchronized和Lock可以保證任一時刻只有一個線程執行該代碼塊,那麼天然就不存在原子性問題了,從而保證了原子性。

二、可見性

對於可見性,Java提供了volatile關鍵字來保證可見性。

當一個共享變量被volatile修飾時,它會保證修改的值會當即被更新到主存,當有其餘線程須要讀取時,它會去內存中讀取新值。

而普通的共享變量不能保證可見性,由於普通共享變量被修改以後,何時被寫入主存是不肯定的,當其餘線程去讀取時,此時內存中可能仍是原來的舊值,所以沒法保證可見性。

另外,經過synchronized和Lock也可以保證可見性,synchronized和Lock能保證同一時刻只有一個線程獲取鎖而後執行同步代碼,而且在釋放鎖以前會將對變量的修改刷新到主存當中。所以能夠保證可見性。

三、有序性

在Java內存模型中,容許編譯器和處理器對指令進行重排序,可是重排序過程不會影響到單線程程序的執行,卻會影響到多線程併發執行的正確性。

在Java裏面,能夠經過volatile關鍵字來保證必定的「有序性」。另外能夠經過synchronized和Lock來保證有序性,很顯然,synchronized和Lock保證每一個時刻是有一個線程執行同步代碼,至關因而讓線程順序執行同步代碼,天然就保證了有序性。

另外,Java內存模型具有一些先天的「有序性」,即不須要經過任何手段就可以獲得保證的有序性,這個一般也稱爲Happens-before規則。若是兩個操做的執行次序沒法從happens-before原則推導出來,那麼它們就不能保證它們的有序性,虛擬機能夠隨意地對它們進行重排序。

參考資料:

Java的原子性&&可見性&&有序性
Java併發編程:volatile關鍵字解析
慕課網高併發實戰(四)- 線程安全性

相關文章
相關標籤/搜索