线程池
为什么要用线程池?
new Thread()的缺点
- 每次new Thread()耗费性能
- 调用new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪。
- 不利于扩展,比如如定时执行、定期执行、线程中断
采用线程池的优点
- 重用存在的线程,减少对象创建、消亡的开销,性能佳
- 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞
- 提供定时执行、定期执行、单线程、并发数控制等功能
#####线程池核心:ThreadPoolExcutor:
- 核心线程数(coreThread)
- 最大线程数(maxThread)
- keepAliveTime,Unit(时间单位)
- Queue(任务缓存队列)
- threadFactory(线程创建工程)
- RejectedExecutionHandler(拒绝执行handler)
- 当前正在执行的线程数 < 核心线程数的时候,新加入的任务就在新线程中执行
- 当前正在执行的线程数 > 核心线程数的时候,新加入的任务放入缓存队列
- 当前正在执行的线程数 >核心线程数的时候,缓存队列满了 且 当前正在执行的线程数<最大线程数,新建线程加入线程池
- 当前正在执行的线程数 >核心线程数的时候,缓存队列满了 且 当前正在执行的线程数=最大线程数,拒绝执行
一句话总结:先用核心线程,再用任务队列,再用”兼职“线程(最大 — 核心 ),最后拒绝执行
四种线程池(cfss)
Excutors.newFixedThreadPool(5);
1
2
3
4
5public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}总共只会创建5个线程, 开始执行五个线程,当五个线程都处于活动状态,再次提交的任务都会加入队列等到其他线程运行结束,当线程处于空闲状态时会被下一个任务复用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36ExecutorService es = Executors.newFixedThreadPool(5);
for (int i = 0; i < 50; i++) {
Runnable runnable = new Runnable() {
public void run() {
Log.d("kb_jay", Thread.currentThread().getName());
}
};
es.execute(runnable);
}
/*
08-14 22:26:43.546 18684-19119/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
08-14 22:26:43.547 18684-19119/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
08-14 22:26:43.547 18684-19116/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
08-14 22:26:43.548 18684-19116/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
08-14 22:26:43.548 18684-19120/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-5
08-14 22:26:43.548 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
08-14 22:26:43.548 18684-19120/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-5
08-14 22:26:43.548 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
pool-1-thread-2
08-14 22:26:43.548 18684-19119/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
08-14 22:26:43.548 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
08-14 22:26:43.548 18684-19118/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-3
08-14 22:26:43.549 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
08-14 22:26:43.549 18684-19116/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
08-14 22:26:43.549 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
08-14 22:26:43.549 18684-19118/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-3
08-14 22:26:43.549 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
08-14 22:26:43.549 18684-19120/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-5
08-14 22:26:43.549 18684-19117/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
08-14 22:26:43.549 18684-19120/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-5
08-14 22:26:43.549 18684-19119/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
08-14 22:26:43.550 18684-19119/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
08-14 22:26:43.550 18684-19116/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
*/Excutors.newCachedThreadPool();
缓存线程池大小是不定值,可以需要创建不同数量的线程,在使用缓存型池时,先查看池中有没有以前创建的线程,如果有,就复用.如果没有,就新建新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务。
1
2
3
4
5
6public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//空闲线程在在60s内不会被回收执行如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35ExecutorService es = Executors.newCachedThreadPool();
for (int i = 0; i < 50; i++) {
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("kb_jay", Thread.currentThread().getName());
}
};
es.execute(runnable);
}
/*
08-14 22:41:57.302 21468-21584/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-2
08-14 22:41:57.302 21468-21582/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
08-14 22:41:57.302 21468-21585/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-3
08-14 22:41:57.303 21468-21586/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-4
08-14 22:41:57.303 21468-21587/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-5
08-14 22:41:57.304 21468-21590/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-6
08-14 22:41:57.304 21468-21591/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-7
08-14 22:41:57.305 21468-21592/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-8
08-14 22:41:57.305 21468-21593/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-9
08-14 22:41:57.306 21468-21594/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-10
08-14 22:41:57.308 21468-21596/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-11
08-14 22:41:57.309 21468-21597/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-12
08-14 22:41:57.309 21468-21598/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-13
08-14 22:41:57.312 21468-21599/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-14
08-14 22:41:57.312 21468-21600/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-15
08-14 22:41:57.314 21468-21601/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-16
08-14 22:41:57.315 21468-21603/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-18
。。。
*/当线程sleep 1s时,几乎每个runnable都会创建一个新的线程,这是因为每个runnable开始执行时都没有cache线程(空闲时间没有超过60s的线程),所以自己要new一个线程出来。
每个runnable的生存期长,不适合。
去掉sleep 1s后:
1 | private void start2() { |
会发现只创建了少量的线程,会有线程的复用情况。这是因为部分runnale开始执行时线程池中有空闲线程且该线程空闲时间没有超过60s,可以复用。
每一个runnable的生存期短,适合。
Excutors.newScheduleThreadPool(5);
这个跟fixedThreadPool相比,只是多了个延迟开始执行的功能。
执行如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33ScheduledExecutorService es = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 50; i++) {
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("kb_jay", Thread.currentThread().getName());
}
};
es.schedule(runnable,5000, TimeUnit.MILLISECONDS);
}
/*
08-14 23:06:03.560 24100-24176/? D/kb_jay: pool-1-thread-1
08-14 23:06:03.560 24100-24177/? D/kb_jay: pool-1-thread-2
08-14 23:06:03.561 24100-24178/? D/kb_jay: pool-1-thread-3
08-14 23:06:03.562 24100-24179/? D/kb_jay: pool-1-thread-4
08-14 23:06:03.562 24100-24180/? D/kb_jay: pool-1-thread-5
08-14 23:06:04.562 24100-24177/? D/kb_jay: pool-1-thread-2
08-14 23:06:04.562 24100-24176/? D/kb_jay: pool-1-thread-1
08-14 23:06:04.562 24100-24178/? D/kb_jay: pool-1-thread-3
08-14 23:06:04.563 24100-24179/? D/kb_jay: pool-1-thread-4
08-14 23:06:04.563 24100-24180/? D/kb_jay: pool-1-thread-5
08-14 23:06:05.563 24100-24178/? D/kb_jay: pool-1-thread-3
08-14 23:06:05.563 24100-24177/? D/kb_jay: pool-1-thread-2
08-14 23:06:05.564 24100-24180/? D/kb_jay: pool-1-thread-5
08-14 23:06:05.564 24100-24176/? D/kb_jay: pool-1-thread-1
。。。
*/schedule(Runnable command,long delay, TimeUnit unit)
创建并执行在给定延迟后启用的一次性操作
**scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnitunit)**
创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;也就是将在 initialDelay
后开始执行,然后在initialDelay+period
后执行,接着在 initialDelay + 2 * period
后执行,依此类推
Excutors.newSingleThreadExcutor();
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
1
2
3
4
5
6public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}执行如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25ExecutorService es = Executors.newSingleThreadExecutor();
for (int i = 0; i < 50; i++) {
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("kb_jay", Thread.currentThread().getName());
}
};
es.execute(runnable);
}
/*
08-14 23:20:28.503 26101-26241/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
08-14 23:20:29.504 26101-26241/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
08-14 23:20:30.506 26101-26241/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
08-14 23:20:31.507 26101-26241/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
08-14 23:20:32.509 26101-26241/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
08-14 23:20:33.511 26101-26241/com.example.kb_jay.kj_thread D/kb_jay: pool-1-thread-1
。。。
*/
####
####