線程是進程中獨立運行的子任務。java
方式一:將類聲明爲 Thread 的子類。該子類應重寫 Thread 類的 run 方法
方式二:聲明實現 Runnable 接口的類。該類而後實現 run 方法
推薦方式二,由於接口方式比繼承方式更靈活,也減小程序間的耦合。api
Thread.currentThread()數組
線程分爲守護線程、用戶線程。線程初始化默認爲用戶線程。
setDaemon(true) 將該線程標記爲守護線程或用戶線程。
特性:設置守護線程,會做爲進程的守護者,若是進程內沒有其餘非守護線程,那麼守護線程也會被銷燬,即便可能線程內沒有運行結束。緩存
某線程a 中啓動另一個線程t,那麼咱們稱線程t是線程a的一個子線程,而 線程a是線程t的父線程。安全
最典型的就是咱們在main方法中 啓動一個線程去執行。其中main方法隱含的main線程爲父線程。多線程
(1)start() 使線程處於就緒狀態,Java虛擬機會調用該線程的run方法;
(2)stop() 中止線程,已過期,存在不安全性:
一是可能請理性的工做得不得完成;
二是可能對鎖定的對象進行「解鎖」,致使數據不一樣步不一致的狀況。
推薦 使用 interrupt() +拋異常 中斷線程。
(3)suspend() 暫停線程,已過期。
resume() 恢復線程,已過期。
suspend 與resume 不建議使用,存在缺陷:
一是可能獨佔同步對象;
二是致使數據不一致。
(4)yield() 放棄當前線程的CPU資源。放棄時間不確認,也有可能剛剛放棄又得到CPU資源。
(5)t.join() 等待該線程t 銷燬終止。併發
一 原子性(互斥性):實現多線程的同步機制,使得鎖內代碼的運行必需先得到對應的鎖,運行完後自動釋放對應的鎖。
二 內存可見性:在同一鎖狀況下,synchronized鎖內代碼保證變量的可見性。
三 可重入性:當一個線程獲取一個對象的鎖,再次請求該對象的鎖時是能夠再次獲取該對象的鎖的。
若是在synchronized鎖內發生異常,鎖會被釋放。框架
總結:dom
(1)synchronized方法 與 synchronized(this) 代碼塊 鎖定的都是當前對象,不一樣的只是同步代碼的範圍異步
(2)synchronized (非this對象x) 將對象x自己做爲「對象監視器」:
a、多個線程同時執行 synchronized(x) 代碼塊,呈現同步效果。
b、當其餘線程同時執行對象x裏面的 synchronized方法時,呈現同步效果。
c、當其餘線程同時執行對象x裏面的 synchronized(this)方法時,呈現同步效果。
(3)靜態synchronized方法 與 synchronized(calss)代碼塊 鎖定的都是Class鎖。Class 鎖與 對象鎖 不是同一個鎖,二者同時使用狀況可能呈異步效果。
(4)儘可能不使用 synchronized(string),是由於string的實際鎖爲string的常量池對象,多個值相同的string對象可能持有同一個鎖。
一 內存可見性:保證變量的可見性,線程在每次使用變量的時候,都會讀取變量修改後的最的值。
二 不保證原子性。
線程間通訊的方式主要爲共享內存、線程同步。
線程同步除了synchronized互斥同步外,也可使用wait/notify實現等待、通知的機制。
(1)wait/notify屬於Object類的方法,但wait和notify方法調用,必須獲取對象的對象級別鎖,即synchronized同步方法或同步塊中使用。
(2)wait()方法:在其餘線程調用此對象的 notify() 方法或 notifyAll() 方法前,或者其餘某個線程中斷當前線程,致使當前線程一直阻塞等待。等同wait(0)方法。
wait(long timeout) 在其餘線程調用此對象的 notify() 方法或 notifyAll() 方法,或者其餘某個線程中斷當前線程,或者已超過某個實際時間量前,致使當前線程等待。 單位爲毫秒。
void wait(long timeout, int nanos) 與 wait(long timeout) 不一樣的是增長了額外的納秒級別,更精細的等待時間控制。
(3)notfiy方法:喚醒在此對象監視器上等待的單個線程。選擇是任意性的,並在對實現作出決定時發生。線程經過調用其中一個 wait 方法,在對象的監視器上等待。
(4)notifyAll方法:喚醒在此對象監視器上等待的全部線程。
須要:wait被執行後,會自動釋放鎖,而notify被執行後,鎖沒有馬上釋放,由synchronized同步塊結束時釋放。
應用場景:簡單的生產、消費問題。
synchronized (lock) {//獲取到對象鎖lock try { lock.wait();//等待通訊信號, 釋放對象鎖lock } catch (InterruptedException e) { e.printStackTrace(); } //接到通訊信號} synchronized (lock) {//獲取到對象鎖lock lock.notify();//通知並喚醒某個正等待的線程 //其餘操做}//釋放對象鎖lock
讓每一個線程都有本身獨立的共享變量,有兩種方式:
一 該實例變量封存在線程類內部;若是該實例變量(非static)是引用類型,存在可能逸出的狀況。
二 就是使用ThreadLocal在任意地方構建變量,即便是靜態的(static)。具備很好的隔離性。
(1)重寫initialValue()方法: 初始化ThreadLocal變量,解決get()返回null問題
(2)InheritableThreadLocal 子線程能夠讀取父線程的值,但反之不行
一個簡單的示例:
private java.util.concurrent.locks.Lock lock = new ReentrantLock(); public void method() { try { lock.lock(); //獲取到鎖lock,同步塊 } finally { lock.unlock();//釋放鎖lock } }
ReentrantLock 比 synchronized 功能更強大,主要體現:
(1)ReentrantLock 具備公平策略的選擇。
(2)ReentrantLock 能夠在獲取鎖的時候,可有條件性地獲取,能夠設置等待時間,頗有效地避免死鎖。
如 tryLock() 和 tryLock(long timeout, TimeUnit unit)
(3)ReentrantLock 能夠獲取鎖的各類信息,用於監控鎖的各類狀態。
(4)ReentrantLock 能夠靈活實現多路通知,即Condition的運用。
1、公平鎖與非公平鎖
ReentrantLock 默認是非公平鎖,容許線程「搶佔插隊」獲取鎖。公平鎖則是線程依照請求的順序獲取鎖,近似FIFO的策略方式。
2、鎖的使用:
(1)lock() 阻塞式地獲取鎖,只有在獲取到鎖後才處理interrupt信息
(2)lockInterruptibly() 阻塞式地獲取鎖,當即處理interrupt信息,並拋出異常
(3)tryLock() 嘗試獲取鎖,無論成功失敗,都當即返回true、false,注意的是即便已將此鎖設置爲使用公平排序策略,tryLock()仍然能夠打開公平性去插隊搶佔。若是但願遵照此鎖的公平設置,則使用 tryLock(0, TimeUnit.SECONDS),它幾乎是等效的(也檢測中斷)。
(4)tryLock(long timeout, TimeUnit unit)在timeout時間內阻塞式地獲取鎖,成功返回true,超時返回false,同時當即處理interrupt信息,並拋出異常。
若是想使用一個容許闖入公平鎖的定時 tryLock,那麼能夠將定時形式和不定時形式組合在一塊兒:
if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
private java.util.concurrent.locks.ReentrantLock lock = new ReentrantLock(); public void testMethod() { try { if (lock.tryLock(1, TimeUnit.SECONDS)) { //獲取到鎖lock,同步塊 } else { //沒有獲取到鎖lock } } catch (InterruptedException e) { e.printStackTrace(); } finally { if (lock.isHeldByCurrentThread()) //若是當前線程持有鎖lock,則釋放鎖lock lock.unlock(); } }
3、條件Condition的使用
條件Condition能夠由鎖lock來建立,實現多路通知的機制。
具備await、signal、signalAll的方法,與wait/notify相似,須要在獲取鎖後方能調用。
private final java.util.concurrent.locks.Lock lock = new ReentrantLock(); private final java.util.concurrent.locks.Condition condition = lock.newCondition(); public void await() { try { lock.lock(); //獲取到鎖lock condition.await();//等待condition通訊信號,釋放condition鎖 //接到condition通訊 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock();//釋放對象鎖lock } }
ReentrantReadWriteLock是對ReentrantLock 更進一步的擴展,實現了讀鎖readLock()(共享鎖)和寫鎖writeLock()(獨佔鎖),實現讀寫分離。讀和讀之間不會互斥,讀和寫、寫和讀、寫和寫之間纔會互斥,提高了讀寫的性能。
讀鎖示例:
private final java.util.concurrent.locks.ReadWriteLock lock = new ReentrantReadWriteLock(); public void method() { try { lock.readLock().lock(); //獲取到讀鎖readLock,同步塊 } finally { lock.readLock().unlock();//釋放讀鎖readLock } }
寫鎖示例:
private final java.util.concurrent.locks.ReadWriteLock lock = new ReentrantReadWriteLock(); public void method() { try { lock.writeLock().lock(); //獲取到寫鎖writeLock,同步塊 } finally { lock.writeLock().unlock(); //釋放寫鎖writeLock } }
(1)同步容器
包括兩部分:
一個是早期JDK的Vector、Hashtable;
一個是它們的同系容器,JDK1.2加入的同步包裝類,使用Collections.synchronizedXxx工廠方法建立。
Map<String, Integer> hashmapSync = Collections.synchronizedMap(new HashMap<>());
同步容器都是線程安全的,一次只有一個線程訪問容器的狀態。
但在某些場景下可能須要加鎖來保護複合操做。
複合類操做如:新增、刪除、迭代、跳轉以及條件運算。
這些複合操做在多線程併發的修改容器時,可能會表現出意外的行爲,
最經典的即是ConcurrentModificationException,
緣由是當容器迭代的過程當中,被併發的修改了內容,這是因爲早期迭代器設計的時候並無考慮併發修改的問題。
其底層的機制無非就是用傳統的synchronized關鍵字對每一個公用的方法都進行同步,使得每次只能有一個線程訪問容器的狀態。這很明顯不知足咱們今天互聯網時代高併發的需求,在保證線程安全的同時,也必須有足夠好的性能。
(2)併發容器
與Collections.synchronizedXxx()同步容器等相比,util.concurrent中引入的併發容器主要解決了兩個問題:
1)根據具體場景進行設計,儘可能避免synchronized,提供併發性。
2)定義了一些併發安全的複合操做,而且保證併發環境下的迭代操做不會出錯。
util.concurrent中容器在迭代時,能夠不封裝在synchronized中,能夠保證不拋異常,可是未必每次看到的都是"最新的、當前的"數據。
Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
ConcurrentHashMap 替代同步的Map即(Collections.synchronized(new HashMap()))。衆所周知,HashMap是根據散列值分段存儲的,同步Map在同步的時候會鎖住整個Map,而ConcurrentHashMap在設計存儲的時候引入了段落Segment定義,同步的時候只須要鎖住根據散列值鎖住了散列值所在的段落便可,大幅度提高了性能。ConcurrentHashMap也增長了對經常使用複合操做的支持,好比"若沒有則添加":putIfAbsent(),替換:replace()。這2個操做都是原子操做。注意的是ConcurrentHashMap 弱化了size()和isEmpty()方法,併發狀況儘可能少用,避免致使可能的加鎖(固然也可能不加鎖得到值,若是map數量沒有變化的話)。
CopyOnWriteArrayList和CopyOnWriteArraySet分別代替List和Set,主要是在遍歷操做爲主的狀況下來代替同步的List和同步的Set,這也就是上面所述的思路:迭代過程要保證不出錯,除了加鎖,另一種方法就是"克隆"容器對象。---缺點也明顯,佔有內存,且數據最終一致,但數據實時不必定一致,通常用於讀多寫少的併發場景。
ConcurrentSkipListMap能夠在高效併發中替代SoredMap(例如用Collections.synchronzedMap包裝的TreeMap)。
ConcurrentSkipListSet能夠在高效併發中替代SoredSet(例如用Collections.synchronzedSet包裝的TreeMap)。
ConcurrentLinkedQuerue是一個先進先出的隊列。它是非阻塞隊列。注意儘可能用isEmpty,而不是size();
CountDownLatch是一個同步輔助類。
一般運用場景:
(1)做爲啓動信號:將計數 1 初始化的 CountDownLatch 用做一個簡單的開/關鎖存器,或入口。
通俗描述:田徑賽跑運動員等待(每位運動員爲一個線程,都在await())的"發令槍",當發令槍countDown(),喊0的時候,全部運動員跳過await()起跑線併發跑起來了。
(2)做爲結束信號:在經過調用 countDown() 的線程打開入口前,全部調用 await 的線程都一直在入口處等待。用 N 初始化的 CountDownLatch 可使一個線程在 N 個線程完成某項操做以前一直等待,或者使其在某項操做完成 N 次以前一直等待。
通俗描述:某裁判,在終點等待全部運動員都跑完,每一個運動員跑完就計數一次(countDown())當爲0時,就能夠往下繼續統計第一人到最後一個撞線的時間。
import java.util.concurrent.CountDownLatch; public class CountDownLatchTest { public static void main(String[] args) throws InterruptedException { final Runnable task = () -> { try { Thread.sleep((long) (Math.random() * 1000)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " end"); }; long time = new CountDownLatchTest().timeTasks(10, task); System.out.println("耗時:" + time + "ms"); } private long timeTasks(int nThreads, final Runnable task) throws InterruptedException { // 一個啓動信號,在 driver 爲繼續執行 worker 作好準備以前,它會阻止全部的 worker 繼續執行。 final CountDownLatch startSignal = new CountDownLatch(1); // 一個完成信號,它容許 driver 在完成全部 worker 以前一直等待。 final CountDownLatch doneSignal = new CountDownLatch(nThreads); for (int i = 0; i < nThreads; i++) { Thread t = new Thread(() -> { try { startSignal.await(); //阻塞於此,一直到startSignal計數爲0,再往下執行 try { task.run(); } finally { doneSignal.countDown(); //doneSignal 計數減一,直到最後一個線程結束 } } catch (InterruptedException ignored) { } }); t.start(); } long start = System.currentTimeMillis(); startSignal.countDown(); // doneSignal 計數減一,爲0,全部task開始併發執行run doneSignal.await(); // 阻塞於此,一直到doneSignal計數爲0,再往下執行 */ long end = System.currentTimeMillis(); return end - start; } }
CyclicBarrier是一個同步輔助類。
CyclicBarrier讓一個線程達到屏障時被阻塞,直到最後一個線程達到屏障時,屏障纔會開門,全部被屏障攔截的線程纔會繼續執行
CyclicBarrier(int parties, Runnable barrierAction)構造函數,用於在全部線程都到達屏障後優先執行barrierAction的run()方法
使用場景:
能夠用於多線程計算之後,最後使用合併計算結果的場景;
通俗描述:某裁判,在終點(await()阻塞處)等待全部運動員都跑完,全部人都跑完就能夠作吃炸雞啤酒(barrierAction),可是隻要一我的沒跑完就都不能吃炸雞啤酒,固然也沒規定他們同時跑(固然也能夠,一塊兒使用CountDownLatch)。
CyclicBarrier與CountDownLatch的區別:
CountDownLatch強調的是一個線程等待多個線程完成某件事,只能用一次,沒法重置;
CyclicBarrier強調的是多個線程互相等待完成,纔去作某個事情,能夠重置。
import java.util.concurrent.CyclicBarrier; public class WorkerThread implements Runnable { private final CyclicBarrier cyclicBarrier; public WorkerThread(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } public static void main(String[] args) { int THREAD_NUM = 5; final CyclicBarrier cyclicBarrier = new CyclicBarrier(THREAD_NUM, new Runnable() { // 當全部線程到達barrier時執行 @Override public void run() { System.out.println("--------------Inside Barrier--------------"); } }); for (int i = 0; i < THREAD_NUM; i++) { new Thread(new WorkerThread(cyclicBarrier)).start(); } } @Override public void run() { try { System.out.println(Thread.currentThread().getName() + " pre-working"); //線程在這裏等待,直到全部線程都到達barrier。 cyclicBarrier.await(); System.out.println(Thread.currentThread().getName() + " working"); } catch (Exception e) { e.printStackTrace(); } } }
更多api:
int await(long timeout, TimeUnit unit) 在全部參與者都已經在此屏障上調用 await 方法以前將一直等待,或者超出了指定的等待時間。
Semaphore信號量是一個計數信號量。
能夠認爲,Semaphore維護一個許可集。若有必要,在許可可用前會阻塞每個 acquire(),而後再獲取該許可。每一個 release() 添加一個許可,從而可能釋放一個正在阻塞的獲取者。
通俗描述:某個車庫只有N個車位,車主們要泊車,請向車庫保安處阻塞 acquire()等待獲取許可證,當得到許可證,車主們才能夠去泊車。當某個車主離開車位的時候,交還許可證release() ,從而其餘阻塞等待的車主有機會得到許可證。
另外:
Semaphore 默認是非公平策略,容許線程「搶佔插隊」獲取許可證。公平策略則是線程依照請求的順序獲取許可證,近似FIFO的策略方式
線程池是一種多線程的處理方式,利用已有線程對象繼續服務新的任務(按照必定的執行策略),而不是頻繁地建立銷燬線程對象,由此提供服務的吞吐能力,減小CPU的閒置時間。具體組成部分包括:
a、線程池管理器(ThreadPool)用於建立和管理線程池,包括建立線程池、銷燬線程池,添加新任務。
b、工做線程(Worker)線程池中的線程,閒置的時候處於等待狀態,能夠循環回收利用。
c、任務接口(Task)每一個任務必須實現的接口類,爲工做線程提供調用,主要規定了任務的入口、任務完成的收尾工做、任務的狀態。
d、等待隊列(Queue)存放等待處理的任務,提供緩衝機制。
Executors框架提供了一些便利的執行策略。
java.util.concurrent.ExecutorService service = java.util.concurrent.Executors.newFixedThreadPool(100);
ExecutorService的生命週期有3個狀態:運行、關閉(shutting down)、中止。
提交任務submit(xxx)擴展了基本方法 Executor.execute(java.lang.Runnable)。
Future<?> submit(Runnable task) 提交一個 Runnable 任務用於執行,並返回一個表示該任務的 Future。
shutdown() 啓動一次順序關閉,執行之前提交的任務,但不接受新任務。
List
一個簡單的示例:
public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(10); for (int i = 0; i < 100; i++) { executorService.submit(() -> System.out.println("哈哈")); } // 若是再也不須要新任務,請適當關閉executorService並拒絕新任務 executorService.shutdown(); }
ThreadPoolExecutor爲Executors的線程池內部實現類。
構造函數詳解:
參數名 | 做用 |
---|---|
corePoolSize | 核心線程池大小 |
maximumPoolSize | 最大線程池大小 |
keepAliveTime | 線程池中超過corePoolSize數目的空閒線程最大存活時間;能夠allowCoreThreadTimeOut(true)使得核心線程有效時間 |
TimeUnit | keepAliveTime時間單位 |
workQueue | 阻塞任務隊列 |
threadFactory | 新建線程工廠 |
RejectedExecutionHandler | 當提交任務數超過maxmumPoolSize+workQueue之和時,任務會交給RejectedExecutionHandler來處理 |
1.當線程池小於corePoolSize時,新提交任務將建立一個新線程執行任務,即便此時線程池中存在空閒線程。
2.當線程池達到corePoolSize時,新提交任務將被放入workQueue中,等待線程池中任務調度執行
3.當workQueue已滿,且maximumPoolSize>corePoolSize時,新提交任務會建立新線程執行任務
4.當提交任務數超過maximumPoolSize時,新提交任務由RejectedExecutionHandler處理
5.當線程池中超過corePoolSize線程,空閒時間達到keepAliveTime時,關閉空閒線程
6.當設置allowCoreThreadTimeOut(true)時,線程池中corePoolSize線程空閒時間達到keepAliveTime也將關閉
一個簡單的示例:
public static void main(String[] args) { java.util.concurrent.ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 10, // corePoolSize 核心線程數 100, // maximumPoolSize 最大線程數 30, // keepAliveTime 線程池中超過corePoolSize數目的空閒線程最大存活時間; TimeUnit.SECONDS,// TimeUnit keepAliveTime時間單位 new LinkedBlockingQueue<>(1000),//workQueue 阻塞任務隊列 Executors.defaultThreadFactory(), // threadFactory 新建線程的工廠 // RejectedExecutionHandler當提交任務數超過maxmumPoolSize+workQueue之和時,任務會交給RejectedExecutionHandler來處理 new ThreadPoolExecutor.AbortPolicy() ); for (int i = 0; i < 100; i++) { threadPoolExecutor.submit(() -> System.out.println("哈哈")); } // 若是再也不須要新任務,請適當關閉threadPoolExecutor並拒絕新任務 threadPoolExecutor.shutdown(); }
public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
V get() throws InterruptedException, ExecutionException 若有必要,等待計算完成,而後獲取其結果。
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 若有必要,最多等待爲使計算完成所給定的時間以後,獲取其結果(若是結果可用)。
FutureTask則是一個RunnableFuture
簡單示例一:
public static void main(String[] args) throws InterruptedException { FutureTask<Integer> future = new FutureTask<>(() -> { // 返回一個值或受檢查的異常 // throw new Exception(); System.out.println("Future task is running..."); Thread.sleep((int) (10000 * Math.random())); return new Random().nextInt(100); }); new Thread(future).start(); //模擬其餘業務邏輯 Thread.sleep(1000); //Integer result = future.get(0, TimeUnit.SECONDS); Integer result = null; try { result = future.get(); System.out.println("Future task finished..."); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("result========" + result); }
簡單示例二,採用Executors:
public static void main(String[] args) throws InterruptedException { java.util.concurrent.ExecutorService threadPoolExecutor = java.util.concurrent.Executors.newCachedThreadPool(); Future<Integer> future = threadPoolExecutor.submit(() -> { // 返回一個值或受檢查的異常 System.out.println("Future task is running..."); Thread.sleep((int) (10000 * Math.random())); //throw new Exception(); return new Random().nextInt(100); }); // 若是再也不須要新任務,請適當關閉threadPoolExecutor並拒絕新任務 threadPoolExecutor.shutdown(); // 模擬其餘業務邏輯 Thread.sleep(1000); // Integer result = future.get(0, TimeUnit.SECONDS); Integer result = null; try { result = future.get(); System.out.println("Future task finished..."); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("result========" + result); }
簡單示例三,採用Executors+CompletionService:
public static void main(String[] args) throws InterruptedException { java.util.concurrent.ExecutorService threadPoolExecutor = java.util.concurrent.Executors.newCachedThreadPool(); java.util.concurrent.CompletionService<Integer> completionService = new java.util.concurrent.ExecutorCompletionService<>(threadPoolExecutor); final int threadNum = 10; for (int i = 0; i < threadNum; i++) { completionService.submit(new MyCallable(i + 1)); } // 若是再也不須要新任務,請適當關閉threadPoolExecutor並拒絕新任務 threadPoolExecutor.shutdown(); // 模擬其餘業務邏輯 Thread.sleep(2000); for (int i = 0; i < threadNum; i++) { try { System.out.println("result========" + completionService.take().get()); } catch (ExecutionException e) { e.printStackTrace(); } } } static class MyCallable implements Callable<Integer> { private final int i; public MyCallable(int i) { super(); this.i = i; } @Override public Integer call() throws Exception { // 返回一個值或受檢查的異常 // throw new Exception(); return i; } }
** 注意的是提交到CompletionService中的Future是按照完成的順序排列的,而不是按照添加的順序排列的。
其基本的特性就是在多線程環境下,當有多個線程同時執行這些類的實例包含的方法時,具備排他性,即當某個線程進入方法,執行其中的指令時,不會被其餘線程打斷,而別的線程就像自旋鎖同樣,一直等到該方法執行完成,才由JVM從等待隊列中選擇一個另外一個線程進入,這只是一種邏輯上的理解。其實是藉助硬件的相關指令來實現的,不會阻塞線程(或者說只是在硬件級別上阻塞了)。其中的類能夠分紅4組
基本類:AtomicInteger、AtomicLong、AtomicBoolean;
引用類型:AtomicReference、AtomicStampedRerence、AtomicMarkableReference;--AtomicStampedReference 或者 AtomicMarkableReference 解決線程併發中,致使的ABA問題
數組類型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray ---數組長度固定不可變,但保證數組上每一個元素的操做絕對安全的
屬性原子修改器(Updater):AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
Updater使用限制:
限制1:操做的目標不能是static類型,前面說到unsafe的已經能夠猜想到它提取的是非static類型的屬性偏移量,若是是static類型在獲取時若是沒有使用對應的方法是會報錯的,而這個Updater並無使用對應的方法。
限制2:操做的目標不能是final類型的,由於final根本無法修改。
限制3:必須是volatile類型的數據,也就是數據自己是讀一致的。
限制4:屬性必須對當前的Updater所在的區域是可見的,也就是private若是不是當前類確定是不可見的,protected若是不存在父子關係也是不可見的,default若是不是在同一個package下也是不可見的。
簡單示例:
private static final AtomicIntegerFieldUpdater<A> ATOMIC_INTEGER_FIELD_UPDATER = AtomicIntegerFieldUpdater.newUpdater(A.class, "intValue"); static class A { volatile int intValue = 100; }
線程安全就是每次運行結果和單線程運行的結果是同樣的,並且其餘的變量的值也和預期的是同樣的。
線程安全就是說多線程訪問同一代碼,不會產生不肯定的結果。
線程安全問題可能是由全局變量和靜態變量引發的,當多個線程對共享數據只執行讀操做,不執行寫操做時,通常是線程安全的;當多個線程都執行寫操做時,須要考慮線程同步來解決線程安全問題。
多個線程操做一個資源的狀況下,致使資源數據先後不一致。這樣就須要協調線程的調度,即線程同步。 解決多個線程使用共通資源的方法是:線程操做資源時獨佔資源,其餘線程不能訪問資源。使用鎖能夠保證在某一代碼段上只有一條線程訪問共用資源。
有兩種方式實現線程同步:
有時候線程之間須要協做和通訊。
實現線程通訊的幾種方式: