您好,登錄后才能下訂單哦!
一,線程的三大特性:原子性、可見性、有序性
1)原子性,即一個操作或者多個操作要么全部執行并且執行的過程不會被任何因素打斷,要么就都不執行。原子性其實就是保證數據一致、線程安全一部分。
2)可見性,即當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看得到修改的值。
3)有序性,即程序執行的順序按照代碼的先后順序執行。
二、JAVA多線程的內存模型
共享內存模型指的就是Java內存模型(簡稱JMM),JMM決定一個線程對共享變量的寫入時,能對另一個線程可見。從抽象的角度來看,JMM定義了線程和主內存之間的抽象關系:線程之間的共享變量存儲在主內存(main memory)中,每個線程都有一個私有的本地內存(local memory),本地內存中存儲了該線程以讀/寫共享變量的副本。本地內存是JMM的一個抽象概念,并不真實存在。它涵蓋了緩存,寫緩沖區,寄存器以及其他的硬件和編譯器優化。
從上圖來看,線程A與線程B之間如要通信的話,必須要經歷下面2個步驟:
1. 首先,線程A把本地內存A中更新過的共享變量刷新到主內存中去。
2. 然后,線程B到主內存中去讀取線程A之前已更新過的共享變量。
總結:什么是Java內存模型:java內存模型簡稱jmm,定義了一個線程對另一個線程可見。共享變量存放在主內存中,每個線程都有自己的本地內存,當多個線程同時訪問一個數據的時候,可能本地內存沒有及時刷新到主內存,所以就會發生線程安全問題。
三、Volatile
Volatile 關鍵字的作用是變量在多個線程之間可見。
1)由于線程之間是不可見的,讀取的是副本,所以會沒有及時讀取到主內存結果。解決辦法使用Volatile關鍵字將解決線程之間可見性, 強制線程每次讀取該值的時候都去“主內存”中取值
2)Volatile非原子性
由于Volatile不具有原子性,需要Atomic原子類。
AtomicInteger atomicInteger = new AtomicInteger(0);
3)volatile與synchronized區別
僅靠volatile不能保證線程的安全性。(非原子性)
a、volatile輕量級,只能修飾變量。synchronized重量級,還可修飾方法
b、volatile只能保證數據的可見性,不能用來同步,因為多個線程并發訪問volatile修飾的變量不會阻塞。
c、synchronized不僅保證可見性,而且還保證原子性,因為,只有獲得了鎖的線程才能進入臨界區,從而保證臨界區中的所有語句都全部執行。多個線程爭搶synchronized鎖對象時,會出現阻塞。
d、線程安全性
線程安全性包括兩個方面,①可見性。②原子性。
從上面自增的例子中可以看出:僅僅使用volatile并不能保證線程安全性。而synchronized則可實現線程的安全性。
四、ThreadLocal
1)ThreadLocal提高一個線程的局部變量,訪問某個線程擁有自己局部變量。當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。
2)ThreadLocal類的接口方法
ThreadLocal類接口很簡單,只有4個方法,我們先來了解一下:
a、void set(Object value)設置當前線程的線程局部變量的值。
b、public Object get()該方法返回當前線程所對應的線程局部變量。
c、public void remove()將當前線程局部變量的值刪除,目的是為了減少內存的占用。需要指出的是,當線程結束后,對應該線程的局部變量將自動被垃圾回收,所以顯式調用該方法清除線程的局部變量并不是必須的操作,但它可以加快內存回收的速度。
d、protected Object initialValue()返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的。這個方法是一個延遲調用方法,在線程第1次調用get()或set(Object)時才執行,并且僅執行1次。ThreadLocal中的缺省實現直接返回一個null。
public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
protected Integer initialValue() {
return 0;
};
};
3)ThreadLoca實現原理
ThreadLoca通過map集合
Map.put(“當前線程”,值);
五、線程池
1)什么是線程池
線程池是指在初始化一個多線程應用程序過程中創建一個線程集合,然后在需要執行新的任務時重用這些線程而不是新建一個線程。線程池中線程的數量通常完全取決于可用內存數量和應用程序的需求。然而,增加可用線程數量是可能的。線程池中的每個線程都有被分配一個任務,一旦任務已經完成了,線程回到池子中并等待下一次分配任務。
2)線程池的作用
基于以下幾個原因在多線程應用程序中使用線程是必須的:
1. 線程池改進了一個應用程序的響應時間。由于線程池中的線程已經準備好且等待被分配任務,應用程序可以直接拿來使用而不用新建一個線程。
2. 線程池節省了CLR 為每個短生存周期任務創建一個完整的線程的開銷并可以在任務完成后回收資源。
3. 線程池根據當前在系統中運行的進程來優化線程時間片。
4. 線程池允許我們開啟多個任務而不用為每個線程設置屬性。
5. 線程池允許我們為正在執行的任務的程序參數傳遞一個包含狀態信息的對象引用。
6. 線程池可以用來解決處理一個特定請求最大線程數量限制問題。
3)線程池的4種創建方式
Java通過Executors(jdk1.5并發包)提供四種線程池,分別為:
1.newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。(常用)
2.newFixedThreadPool 創建一個定長線程池,可控制線程最大并發數,超出的線程會在隊列中等待。
3.newScheduledThreadPool 創建一個定長線程池,支持定時及周期性任務執行。
4.newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。