面試關於多執行緒
1. 我們來溫習下多執行緒的實現方式
1 繼承 thread
public class myThread extends Thread{
@override
run{
system.out.println("你好我是執行緒");
}
}
啟動執行緒
myThread mt = new myThread ();
mt.start();
2 實現介面Runnable
public class myThread implment Runnable{ @override run{ system.out.println("你好我是執行緒"); } }
啟動執行緒
myThread mt = new myThread ();
Thread tt = new Thread (mt);
tt.start();
3 實現callable
public class mycall implement callable<Integer>{
@override
public Integer call{
system.out.println("你好我是執行緒");
retrun 0;
}
}
啟動執行緒
mycall mc = new mycall(); FutureTask ft = new FutureTask(mc); Thread thread =new Thread(ft); thread.start(); system.out.println(ft.get());
4 使用執行緒池
啟動方法
ExecutorService theadPool = Executor.newFixedThreadPool(4); ArrayList<Future<Status>> arrayList=new ArrayList<Future<Status>>(); for (int i = 1; i <=4; i++) { mycall mc = new mycall(); Future<Integer> ft = theadPool.submit(mc); arrayList.add(ft); } theadPool.shutdown; for (Future<Status> future2 : arrayList) { System.err.println(future2.get().name()); }
2 . 你有沒有使用concurrent工具類
Java 5 添加了一個新的包到 Java 平臺,java.util.concurrent 包
點這裡
介面Executor Runnable 是中有run方法
public interface Executor {
void execute(Runnable command);//執行一個Runnable物件
}
ExecutorService 是Executor介面的實現類,其子類有AbstractExecutorService,SchedualedExecutorService
Executors 是一個工具類,提供方法返回ExecutorService來建立執行緒池
點這裡
Executors 所建立的執行緒池實際使用ExecutorService的子類ThreadPoolExecutor,我們來看個例子
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
所以Executors 只是一個幫我們填好引數的呼叫ThreadPoolExecutor的工具類,看到一些文章中阿里釋出的java守則中建議不使用Executors來建立執行緒池,所以我們等下再來認真學習下ThreadPoolExecutor內的引數。
Executors 的四種執行緒池
newCachedThreadPool 建立有快取的執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。
newFixedThreadPool 建立一個定長的執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。
newScheduledThreadPool 建立一個定長執行緒池,支援定時及週期性任務執行。
newSingleThreadExecutor 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。
例子:
ExecutorService theadpool = Exectors.newFixThreadPool(5);
Mycall mycall = new Mycall();
Future <Status> future = theadpool.submit(mycall );
執行緒池的5種狀態:
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
- RUNNING 執行
狀態說明:執行緒池處在RUNNING狀態時,能夠接收新任務,以及對已新增的任務進行處理。 - SHUTDOWN 停止執行
狀態說明:執行緒池處在SHUTDOWN狀態時,不接收新任務,但能處理已新增的任務。 - STOP
狀態說明:執行緒池處在STOP狀態時,不接收新任務,不處理已新增的任務,並且會中斷正在處理的任務。 - TIDYING
當執行緒池在STOP狀態下,執行緒池中執行的任務為空時,就會由STOP -> TIDYING。 - TERMINATED
執行緒池處在TIDYING狀態時,執行完terminated()之後,就會由 TIDYING -> TERMINATED。
關閉執行緒池:
shutdown() 停止加入新的執行緒任務
shutdownNow() 中斷在執行的執行緒任務,清空快取佇列
執行緒池的工作過程如下:
- 執行緒池剛建立時,裡面沒有一個執行緒。任務佇列是作為引數傳進來的。不過,就算佇列裡面有任務,執行緒池也不會馬上執行它們。
- 當呼叫 execute() 方法新增一個任務時,執行緒池會做如下判斷:
1、 如果正在執行的執行緒數量小於 corePoolSize,那麼馬上建立執行緒執行這個任務;
2 、 如果正在執行的執行緒數量大於或等於 corePoolSize,那麼將這個任務放入佇列;
3 、如果這個時候佇列滿了,而且正在執行的執行緒數量小於maximumPoolSize,那麼建立執行緒執行這個任務;
4 、如果佇列滿了,切正在執行的執行緒數量大於等於maximumPoolSize,那麼就執行拒絕策略。 - 當一個執行緒完成任務時,它會從佇列中取下一個任務來執行。
- 當一個執行緒無事可做,超過一定的時間(keepAliveTime)時,執行緒池會判斷,如果當前執行的執行緒數大於 corePoolSize,那麼這個執行緒就被停掉。所以執行緒池的所有任務完成後,它最終會收縮到 corePoolSize 的大小。
3 如何控制執行緒執行順序
1 Executors.newSingleThreadExecutor
newSingleThreadExecutor產生一個單執行緒的執行緒池,而這個執行緒池的底層原理就是一個先進先出(FIFO)的佇列。按照加入執行緒池的順序執行。
ExecutorService theadpool = Exectors.newSingleThreadExecutor;
Mycall mycall = new Mycall();
Future <Status> future = theadpool.submit(mycall );
2 join
使用執行緒的join方法,該方法的作用是“等待執行緒執行結束”,即join()方法後面的程式碼塊都要等待現場執行結束後才能執行。 實質是將主執行緒阻塞,等到執行緒執行完。
myThread my = new myThread();
Thread thread = new Thread(my);
thread.start();
thread.join;
4 ThreadPoolExecutor
點這裡更好的理解執行緒池
ThreadPoolExecutor是ExecutorService的具體實現類,建立執行緒池一般使用這個類的方法,
例如:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
我們來解釋下其中的引數:
- corePoolSize:執行緒池的核心執行緒數,執行緒池中執行的執行緒數也永遠不會超過 corePoolSize 個,預設情況下可以一直存活。可以通過設定allowCoreThreadTimeOut為True,此時 核心執行緒數就是0,此時
- keepAliveTime控制所有執行緒的超時時間。
- maximumPoolSize:執行緒池允許的最大執行緒數;
- keepAliveTime: 指的是空閒執行緒結束的超時時間。當一個執行緒無事可做,超過keepAliveTime時,執行緒池會判斷如果當前執行的執行緒數大約corePoolSize,那麼這個執行緒就會被停掉。
- unit :是一個列舉,表示 keepAliveTime 的單位;
- workQueue:表示存放任務的BlockingQueue<Runnable佇列。
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;
ArrayBlockingQueue
ArrayBlockingQueue是一個用陣列實現的有界阻塞佇列。此佇列按照先進先出(FIFO)的原則對元素進行排序。
LinkedBlockingQueue
LinkedBlockingQueue是一個用連結串列實現的有界阻塞佇列。此佇列的預設和最大長度為Integer.MAX_VALUE。此佇列按照先進先出的原則對元素進行排序。
SynchronousQueue
SynchronousQueue是一個不儲存元素的阻塞佇列。每一個put操作必須等待一個take操作,否則不能繼續新增元素。
瞭解了佇列的基本知識,我們再回頭看下四種執行緒池
- newCachedThreadPool
使用佇列SynchronousQueue,多大執行緒數為Integer.MAX_VALUE,超時時間為60L, 時間單位為TimeUnit.SECONDS,
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- newFixedThreadPool
使用佇列LinkedBlockingQueue
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- newScheduledThreadPool
使用佇列DelayedWorkQueue 實現類是ScheduledThreadPoolExecutor,核心執行緒大小corePoolSize
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
- newSingleThreadExecutor
使用LinkedBlockingQueue佇列實現,最大執行緒數為1
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
下面只是簡單的寫一下,具體的事例有時間了再補上
5 執行緒間通訊及同步
- synchronized
可以鎖住資源不被其他執行緒修改 - wait/notify機制
wai/notify 是object的方法
6 阻塞佇列和非阻塞佇列
點這裡
實現佇列的阻塞實質上對佇列中的資料lock
而非阻塞佇列沒有lock
使用阻塞佇列與非阻塞佇列實現生產者和消費者。
7 執行緒池的拒絕策略
這個好
有四種拒絕策略
- 1 DiscardPolicy
放棄要加入佇列的任務 - 2 DiscardOldestPolicy
放棄在佇列最前端的任務 - 3 RejectedExecutionException
丟擲異常 - 4 CallerRunsPolicy
直接執行execute
如何停止正在執行的執行緒
點這裡
1 使用stop
2 使用interrupt
3 使用退出標識,使得執行緒正常退出
那麼如何停止執行緒池中的執行緒呢
點這裡
shutdown
shutdownNow