1.什麼是多線程?java
多線程是爲了使得多個線程並行的工做以完成多項任務,以提升系統的效率。線程是在同一時間須要完成多項任務的時候被實現的。安全
2.瞭解多線程多線程
瞭解多線程以前咱們先搞清楚幾個重要的概念!併發
如上圖所示:對咱們的項目有一個主內存,這個主內存裏面存放了咱們的共享變量、方法區、堆中的對象等。性能
3.線程的工做過程優化
每當咱們開啓一個線程的時候,線程會爲咱們開闢一塊工做內存,將主內存中的共享變量複製一個副本存入工做內存中,並協調方法區生成棧針,以及對堆的引用(指針)。操作系統
若是在執行過程當中線程對工做內存中的共享變量進行的修改操做,此時會向主內存回寫咱們修改的變量。線程
4.多線程帶來的問題指針
咱們模擬這樣一個場景:對象
有十個用戶同時購票,可是系統中只剩下了8張票,當每一個用戶同時開啓本身的線程,將主內存中8張票複製到工做內存中,在方法中,會判斷票數是否知足要求,此時,十個線程都判斷知足,都要對票數進行操做。
當用戶一操做後,票數=8-1=7,將數據回寫至主內存。
用戶二操做後,用戶二的本地內存中票數爲8,則修改後票數=8-1=7,繼續回寫至主內存,
以此下去,在咱們假設十個用戶同時開啓線程的狀況下最後主內存中的票數確定是7,並且十個用戶均出票成功,出現了超賣的狀況,這在現實場景是很危險的事!
5.多線程的特性
有序性:程序執行的順序按照代碼的前後順序執行。
可見性:當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其餘線程可以當即看獲得修改的值。
若兩個線程在不一樣的cpu,那麼線程1改變了i的值還沒刷新到主存,線程2又使用了i,那麼這個i值確定仍是以前的,線程1對變量的修改線程沒看到這就是可見性問題。
原子性:即一個操做或者多個操做要麼所有執行而且執行的過程不會被任何因素打斷,要麼就都不執行。
在程序編譯到執行的過程當中,程序會通過屢次重排序,源代碼->編譯器優化重排序->指令級並行重排序->內存系統重排序->最終執行的指令序列,
也就是說咱們編寫的代碼,通過這一連串的重排序後,代碼極可能就和咱們寫的順序不一致了,可是咱們的操做系統等會保證咱們
最終執行的指令序列與咱們的源代碼的結果保持一致,咱們的操做系統是能夠保證單線程的有序性的。
6.怎麼解決多線程併發帶來的問題?
何時須要使用多線程?
競態條件:檢查後執行是否知足決定下一步。
方法一:加鎖
1.監視器鎖synchronized,它確保了每一個線程是隔離的,並且只有當一個線程執行進入帶有synchronized的方法中時加鎖,
當該線程爲結束此方法解鎖時,其它線程將掛起,直到該線程解鎖後其它線程才能繼續執行下去。它可以保證上述三大特性:有序性、可見性、原子性。
JMM定義內存訪問規範,實現有序性、可見性、原子性,共八大規則,你們能夠上網瞭解JMM詳細規則信息。
同步機制:
監視器鎖synchronized
顯示鎖ReentrantLock、ReadWriteLock
原子變量AtomicInteger、AtomicLong、AtomicBoolean
Volatile
問題:遇到同步問題如何選擇具體的實現方式?
監視器鎖在jdk1.5之後,性能獲得了很大的提高,而且在java版本更新中一直在被優化,並且synchronized鎖能夠自動實現加鎖與解鎖。
顯示鎖須要咱們手動解鎖、加鎖,容易失誤致使死鎖。
在考慮性能時,推薦使用監視器鎖,當考慮功能時,推薦使用顯示鎖,顯示鎖擁有更多自定義的選擇。
方法二:線程封閉
什麼是線程封閉?
當訪問共享的可變數據時,一般須要同步,一種避免同步的方式就是不共享數據,若是僅在單線程內訪問數據,就不須要同步,這種技術稱爲線程封閉。
若是使用線程封閉:1.棧封閉:線程爲跳用方法生成棧針時局部變量就使用了線程封閉。
2.ThreadLocal --> 只有當前線程能使用。
方法三:不可變對象必定是線程安全的。
最佳方案:使用線程安全的對象是實現線程安全的。 java.util.concurrent包下的類。