Java中提供了一系列和鎖相關(guān)的工具:synchronized茉唉、ReentrantLock乒验、AtomicXXX類吩翻,它們都能幫助解決我們進(jìn)行多線程操作時(shí)并發(fā)問題海蔽,之前我們也提到了內(nèi)核線程模型,Hotspot虛擬機(jī)線程的創(chuàng)建都是通過內(nèi)核線程提供的輕量級進(jìn)程API創(chuàng)建的输硝,反復(fù)的創(chuàng)建線程會(huì)不斷的由OS線程調(diào)度今瀑,這也會(huì)消耗大量CPU性能
爭對創(chuàng)建線程,除了使用Thread之外点把,Java還提供了線程池類橘荠,它們最終都實(shí)現(xiàn)的Executor接口,JDK1.7中的繼承實(shí)現(xiàn)關(guān)系如下:
一郎逃、使用線程池與Thread對比
使用Thread創(chuàng)建10000個(gè)線程哥童,并運(yùn)行,記錄時(shí)間:
public class ThreadPoolTest {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
List<Thread> threads = new ArrayList<>(10000);
for (int i = 0; i < 10000; i++) {
Thread thread = new Thread();
threads.add(thread);
thread.start();
}
for(Thread t:threads){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("ms:"+(System.currentTimeMillis() - startTime));
}
}
結(jié)果:
ms:1761
使用線程池:
public class ThreadPoolTest2 {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
int size = 10000;
ExecutorService executorService = Executors.newSingleThreadExecutor();
final CountDownLatch countDownLatch = new CountDownLatch(size);
for (int i = 0; i < size; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
countDownLatch.countDown();
}
});
}
try {
countDownLatch.await();//等待10000計(jì)數(shù)結(jié)束
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.shutdown();
System.out.println("ms:" + (System.currentTimeMillis() - startTime));
}
}
結(jié)果:
ms:69
兩者相差的時(shí)間為100倍以上褒翰,這邊使用的線程池回頭再來看贮懈,由這個(gè)例子可以看出,線程的創(chuàng)建到運(yùn)行過程优训,會(huì)花費(fèi)大量的cpu時(shí)間朵你,爭對需要大量并發(fā)的操作,使用線程池可以大大提高程序性能
二揣非、ThreadPoolExecutor
1.ThreadPoolExecutor執(zhí)行任務(wù)圖解
Executors是個(gè)幫助類撬呢,內(nèi)部實(shí)際使用的是ThreadPoolExecutor,構(gòu)造函數(shù)如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
ThreadPoolExecutor大致的執(zhí)行場景如下:
2.ThreadPoolExecutor參數(shù)
再來看構(gòu)造函數(shù)中各個(gè)參數(shù)的含義:
corePoolSize:核心線程最大數(shù)妆兑,當(dāng)有新任務(wù)時(shí)魂拦,如果小于該值,那么會(huì)直接創(chuàng)建線程或使用空閑的核心線程執(zhí)行任務(wù)
maximumPoolSize:最大線程數(shù)搁嗓,需大于核心線程數(shù)芯勘,非核心線程最大數(shù) = 最大線程數(shù) - 核心線程最大數(shù),當(dāng)核心線程全部創(chuàng)建并都在執(zhí)行任務(wù)腺逛,那么新任務(wù)使用非核心線程執(zhí)行荷愕,同樣使用核心線程創(chuàng)建機(jī)制:創(chuàng)建線程或使用空閑的線程,非核心線程可以指定死亡時(shí)間棍矛,相當(dāng)于臨時(shí)工
keepAliveTime:非核心線程的保持存活時(shí)間安疗,當(dāng)一個(gè)非核心線程持續(xù)keepAliveTime時(shí)間都處于空閑狀態(tài),那么使它消亡
unit:保持存活時(shí)間的時(shí)間單位
workQueue:任務(wù)隊(duì)列够委,當(dāng)線程總數(shù)到達(dá)最大荐类,并且所有線程都有任務(wù)在執(zhí)行,那么新任務(wù)可以進(jìn)入該隊(duì)列茁帽,等待有線程空閑時(shí)執(zhí)行隊(duì)列中的任務(wù)玉罐。通過Executors創(chuàng)建的任務(wù)隊(duì)列屈嗤,都沒有最大值,所以當(dāng)遇到服務(wù)器并發(fā)量大的情況吊输,容易OOM饶号,安卓開發(fā)中不需要考慮這個(gè)問題
threadFactory:創(chuàng)建線程工廠,一般使用默認(rèn)的即可
handler:拒絕策略季蚂,當(dāng)觸發(fā)拒絕時(shí)茫船,執(zhí)行該對象策略策略
3.任務(wù)隊(duì)列
任務(wù)隊(duì)列實(shí)現(xiàn)BlockingQueue接口,JDK提供了以下4種:
- ArrayBlockingQueue: 是一個(gè)基于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列扭屁,此隊(duì)列按 FIFO(先進(jìn)先出)原則對元素進(jìn)行排序算谈。
- LinkedBlockingQueue: 一個(gè)基于鏈表結(jié)構(gòu)的阻塞隊(duì)列,此隊(duì)列按FIFO(先進(jìn)先出)排序元素疯搅,吞吐量通常要高于ArrayBlockingQueue濒生。靜態(tài)工廠方法Executors.newFixedThreadPool()使用了這個(gè)隊(duì)列埋泵。
- SynchronousQueue: 一個(gè)不存儲元素的阻塞隊(duì)列幔欧。每個(gè)插入操作必須等到另一個(gè)線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài)丽声,吞吐量通常要高于LinkedBlockingQueue礁蔗,靜態(tài)工廠方法Executors.newCachedThreadPool使用了這個(gè)隊(duì)列。
- PriorityBlockingQueue: 一個(gè)具有優(yōu)先級的無限阻塞隊(duì)列雁社。
4.拒絕策略
- DiscardPolicy:丟棄當(dāng)前將要加入隊(duì)列的任務(wù)本身浴井。
- DiscardOldestPolicy:丟棄任務(wù)隊(duì)列中最舊任務(wù)。(丟棄最舊任務(wù)也不是簡單的丟棄最舊的任務(wù)霉撵,而是有一些額外的處理)
- AbortPolicy:拋出異常的方式磺浙。
- CallerRunsPolicy:不進(jìn)入線程池執(zhí)行,由調(diào)用者線程執(zhí)行徒坡。
三撕氧、Executors
后臺一般不允許使用Executors,但我們安卓開發(fā)是可以使用的喇完,接下來來使用Executors三種線程池伦泥,有了上面ThreadPoolExecutor的了解,理解起來很簡單
public class ThreadPoolTest3 {
public static void main(String[] args) {
ExecutorService executorService1 = Executors.newCachedThreadPool();
ExecutorService executorService2 = Executors.newFixedThreadPool(10);
ExecutorService executorService3 = Executors.newSingleThreadExecutor();
for (int i = 1; i <= 100; i++) {
executorService1.execute(new TestRunnable(i));
// executorService2.execute(new TestRunnable(i));
// executorService3.execute(new TestRunnable(i));
}
}
static class TestRunnable implements Runnable {
int i = 0;
public TestRunnable(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
1.newCachedThreadPool
newCachedThreadPool方法如下:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
它和直接創(chuàng)建線程類似锦溪,可以創(chuàng)建無限的線程
2.newFixedThreadPool
newFixedThreadPool方法如下:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
它擁有指定大小的核心線程數(shù)不脯,并且只有核心線程,以及一個(gè)無限大小的任務(wù)隊(duì)列
3.newSingleThreadExecutor
newSingleThreadExecutor方法如下:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
它僅有一個(gè)核心線程刻诊,以及一個(gè)無限大小的任務(wù)隊(duì)列防楷,所以相當(dāng)于單線程執(zhí)行任務(wù)隊(duì)列中的任務(wù)