實現多線程的方法
1.實現Thread接口
public class MyThread extends Thread {
public void run() {
System.out.println("MyThread.run()");
}
}
MyThread myThread1 = new MyThread();
myThread1.start();
2.實現Runnable接口創建線程
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
}
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
3.實現
線程池
- 創建線程池的代碼
// 第一種是可變大小線程池,按照任務數來分配線程,
ExecutorService e = Executors.newCachedThreadPool();
// 第二種是單線程池,相當于FixedThreadPool(1)
ExecutorService e = Executors.newSingleThreadExecutor();
// 第三種是固定大小線程池。
ExecutorService e = Executors.newFixedThreadPool(3);
e.execute(new MyRunnableImpl());
2.ThreadPoolExecutor
ExecutorService 類內部是通過ThreadPoolExecutor實現的,本質上,他們都是ThreadPoolExecutor類的各種實現版本。
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize
核心線程數,默認情況下核心線程會一直存活,即使處于閑置狀態也不會受存keepAliveTime限制。除非將allowCoreThreadTimeOut設置為true。
- maximumPoolSize
線程池所能容納的最大線程數。超過這個數的線程將被阻塞。當任務隊列為沒有設置大小的LinkedBlockingDeque時,這個值無效。
- keepAliveTime
非核心線程的閑置超時時間,超過這個時間就會被回收。
- unit
指定keepAliveTime的單位,如TimeUnit.SECONDS。當將allowCoreThreadTimeOut設置為true時對corePoolSize生效。
- workQueue
線程池中的任務隊列.常用的有三種隊列,SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue。
ArrayBlockingQueue // 數組實現的阻塞隊列,數組不支持自動擴容。所以當阻塞隊列已滿
// 線程池會根據handler參數中指定的拒絕任務的策略決定如何處理后面加入的任務
LinkedBlockingQueue // 鏈表實現的阻塞隊列,默認容量Integer.MAX_VALUE(不限容),
// 當然也可以通過構造方法限制容量
SynchronousQueue // 零容量的同步阻塞隊列,添加任務直到有線程接受該任務才返回
// 用于實現生產者與消費者的同步,所以被叫做同步隊列
PriorityBlockingQueue // 二叉堆實現的優先級阻塞隊列
DelayQueue // 延時阻塞隊列,該隊列中的元素需要實現Delayed接口
// 底層使用PriorityQueue的二叉堆對Delayed元素排序
// ScheduledThreadPoolExecutor底層就用了DelayQueue的變體"DelayWorkQueue"
// 隊列中所有的任務都會封裝成ScheduledFutureTask對象(該類已實現Delayed接口)
- threadFactory
線程工廠,提供創建新線程的功能。ThreadFactory是一個接口,只有一個方法.通過線程工廠可以對線程的一些屬性進行定制。 - RejectedExecutionHandler
RejectedExecutionHandler也是一個接口,只有一個方法.當線程池中的資源已經全部使用,添加新線程被拒絕時,會調用RejectedExecutionHandler的rejectedExecution方法。
3.線程池規則
3.1任務隊列沒有大小限制:
- 如果線程數量<=核心線程數量,那么直接啟動一個核心線程來執行任務,不會放入隊列中。
- 如果線程數量>核心線程數,但<=最大線程數,并且任務隊列是LinkedBlockingDeque的時候,超過核心線程數量的任務會放在任務隊列中排隊。
- 如果線程數量>核心線程數,但<=最大線程數,并且任務隊列是SynchronousQueue的時候,線程池會創建新線程執行任務,這些任務也不會被放在任務隊列中。這些線程屬于非核心線程,在任務完成后,閑置時間達到了超時時間就會被清除。
- 如果線程數量>核心線程數,并且>最大線程數,當任務隊列是LinkedBlockingDeque,會將超過核心線程的任務放在任務隊列中排隊。也就是當任務隊列是LinkedBlockingDeque并且沒有大小限制時,線程池的最大線程數設置是無效的,他的線程數最多不會超過核心線程數。
- 如果線程數量>核心線程數,并且>最大線程數,當任務隊列是SynchronousQueue的時候,會因為線程池拒絕添加任務而拋出異常。
3.2 任務隊列大小有限時
- 當LinkedBlockingDeque塞滿時,新增的任務會直接創建新線程來執行,當創建的線程數量超過最大線程數量時會拋異常。
- SynchronousQueue沒有數量限制。因為他根本不保持這些任務,而是直接交給線程池去執行。當任務數量超過最大線程數時會直接拋異常。
4BlockingQueue
當任務隊列是LinkedBlockingDeque,超出核心線程數,會存入隊列,隊列存滿后,開啟新線程執行,線程數超過最大線程數,拋出異常。
SynchronousQueue沒有數量限制。因為他根本不保持這些任務,而是直接交給線程池去執行。當任務數量超過最大線程數時會直接拋異常。
ThreadLocal
采用空間換時間,它用于線程間的數據隔離,為每一個使用該變量的線程提供一個副本,每個線程都可以獨立地改變自己的副本,而不會和其他線程的副本沖突。
ThreadLocal類中維護一個Map,用于存儲每一個線程的變量副本,Map中元素的鍵為線程對象,而值為對應線程的變量副本。
ThreadLocal在Spring中發揮著巨大的作用,在管理Request作用域中的Bean、事務管理、任務調度、AOP等模塊都出現了它的身影。
Spring中絕大部分Bean都可以聲明成Singleton作用域,采用ThreadLocal進行封裝,因此有狀態的Bean就能夠以singleton的方式在多線程中正常工作了。
ReentrantLock 和Synchronized的區別
ReentrantLock 擁有Synchronized相同的并發性和內存語義,此外還多了 鎖投票,定時鎖等候和中斷鎖等候
fail-fast 機制
當多個線程對同一個集合的內容進行操作時,就可能會產生fail-fast事件。例如:當某一個線程A通過iterator去遍歷某集合的過程中,若該集合的內容被其他線程所改變了;那么線程A訪問集合時,就會拋出ConcurrentModificationException異常,產生fail-fast事件。
Volatile和Synchronized四個不同點
1 粒度不同,前者針對變量 ,后者鎖對象和類
2 syn阻塞,volatile線程不阻塞
3 syn保證三大特性,volatile不保證原子性
4 syn編譯器優化,volatile不優化 volatile具備兩種特性: