13.线程池
1.什么是线程池
- 一个创建线程的容器,可以通过该容器去创建不同业务场景的线程来完成不同的需求
2.为什么需要线程池
-
线程缺少统一的管理,占用过多的系统资源
-
缺乏更多功能.比如定时执行、定期执行等
-
使用线程池的优势
- 可以重用存在的线程,减少对象的创建、消亡的开销
- 有效的控制并发数,提高系统资源的使用率
- 线程池可以实现定时执行、定期执行等功能.
3.如何创建线程池
- 线程池所在的包都在java.util.concurrent包下,顶级的接口Executors,真正的线程池接口是ExecutorService
| 方法名 | 说明 |
|---|---|
| newCachedThreadPool() | 创建了一个可缓存的线程池,有任务才会创建新的线程 |
| newSingleThreadPool() | 创建一个单线程,线程池中只有一个线程 |
| newEixedThreadPool(int numThreads) | 创建一个固定长度的线程池,空闲线程会一直保留,numThreads设定线程的数量 |
| newScheduledThreadPool(int corePoolSize) | 创建一个固定长度的线程池,而且一延迟或定时的方式来执行线程 |
案例1:newCachedThreadPool()创建了一个可缓存的线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
//创建了一个有缓存线程池,有任务时才会创建更多的线程
ExecutorService cachedThread = Executors.newCachedThreadPool();
for (int i = 1; i <= 10; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThread.execute(new MyRunable(i));
}
}
}
结果
pool-1-thread-1执行了1
pool-1-thread-1执行了2
pool-1-thread-1执行了3
pool-1-thread-1执行了4
pool-1-thread-1执行了5
pool-1-thread-1执行了6
pool-1-thread-1执行了7
pool-1-thread-1执行了8
pool-1-thread-1执行了9
pool-1-thread-1执行了10
10个任务被同一个线程执行了,因此减少了线程的创建和销毁,大大提高了效率
ExecutorService cachedThread = Executors.newCachedThreadPool();创建了一个有缓存线程池,当去执行1-10这个任务的时候,忙的过来的时候只会创建一个线程,但是该线程忙不过来的时候,会根据任务创建出更多的线程来完成任务
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
//创建了一个有缓存线程池,有任务时才会创建更多的线程
ExecutorService cachedThread = Executors.newCachedThreadPool();
for (int i = 1; i <= 10; i++) {
// try {
// Thread.sleep(500);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
cachedThread.execute(new MyRunable(i));
}
}
}
结果
pool-1-thread-1执行了1
pool-1-thread-2执行了2
pool-1-thread-3执行了3
pool-1-thread-5执行了5
pool-1-thread-6执行了6
pool-1-thread-2执行了10
pool-1-thread-7执行了7
pool-1-thread-4执行了4
pool-1-thread-8执行了8
pool-1-thread-9执行了9
因为线程休眠被取消了,执行的时间变短了,线程池让更多的线程参与到任务里面来,因此线程参与到任务里面来了,因此创建了9个线程参与进来,有的线程在执行完任务后,会去接新的任务,比如2号线程执行了两次
- 当多个线程在执行任务时,如果有线程阻塞,该线程池会创建出更多的线程来完成任务,提高性能。
案例2:newSingleThreadExecutor()创建单线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
//创建了一个只有一个线程的线程池,叫单线程池
ExecutorService cachedThread = Executors.newSingleThreadExecutor();
for (int i = 1; i <= 10; i++) {
cachedThread.execute(new MyRunable(i));
}
}
}
结果
pool-1-thread-1执行了1
pool-1-thread-1执行了2
pool-1-thread-1执行了3
pool-1-thread-1执行了4
pool-1-thread-1执行了5
pool-1-thread-1执行了6
pool-1-thread-1执行了7
pool-1-thread-1执行了8
pool-1-thread-1执行了9
pool-1-thread-1执行了10
案例3:newFixedThreadPool(3)创建了固定线程个数的线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args) {
//创建了固定线程个数的线程池
ExecutorService cachedThread = Executors.newFixedThreadPool(3);
for (int i = 1; i <= 10; i++) {
cachedThread.execute(new MyRunable(i));
}
}
}
结果
pool-1-thread-1执行了1
pool-1-thread-3执行了3
pool-1-thread-2执行了2
pool-1-thread-3执行了5
pool-1-thread-1执行了4
pool-1-thread-3执行了7
pool-1-thread-2执行了6
pool-1-thread-3执行了9
pool-1-thread-1执行了8
pool-1-thread-2执行了10
案例4:scheduleAtFixedRate(new MyRunable(), 5, 2, TimeUnit.SECONDS)创建定时线程池
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
/**
* 参数1:需要执行线程任务的对象
* 参数2:延迟的单位
* 参数3:间隔时间单位(每隔这个事件单位执行一次)
* 参数4:时间单位,具体参见TimeUnit枚举,我在这设置了秒
*/
scheduledThreadPool.scheduleAtFixedRate(new MyRunable(), 5, 2, TimeUnit.SECONDS);
}
}
结果
pool-1-thread-1延迟5秒执行每两秒执行一下该任务
pool-1-thread-1延迟5秒执行每两秒执行一下该任务
pool-1-thread-2延迟5秒执行每两秒执行一下该任务
pool-1-thread-2延迟5秒执行每两秒执行一下该任务
pool-1-thread-3延迟5秒执行每两秒执行一下该任务
pool-1-thread-1延迟5秒执行每两秒执行一下该任务
pool-1-thread-2延迟5秒执行每两秒执行一下该任务
pool-1-thread-3延迟5秒执行每两秒执行一下该任务
pool-1-thread-3延迟5秒执行每两秒执行一下该任务
pool-1-thread-3延迟5秒执行每两秒执行一下该任务
案例5:创建自定义线程池
-
如果上面定义好的线程池类型不符合我们的业务要求,那么我们可以自定义线程池
-
情况1
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class Test { public static void main(String[] args) { //创建一个自定义的线程池 /** * 参数1:开启线程的固定数量为5个 * 参数2:当任务数量多的时候,最大的线程数量最大为7(有两个临时工) * 参数3:临时线程等待接收任务的时间,如果超出300秒没有接收到新任务会自动销毁 * 参数4:时间单位,设置了秒 * 参数5:等待执行的任务序列为4个(其中有4个任务需要等待线程池中7个线程执行完任务再去执行) */ ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 7, 300, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4)); for (int i = 1; i <= 12; i++) { executor.execute(new MyRunable(i)); System.out.println("线程池中的线程数:"+executor.getPoolSize()+ ",队列中等待执行任务数:"+executor.getQueue().size() +",已经执行完的任务数:"+executor.getMaximumPoolSize()); } executor.isShutdown(); } } -
结果
正在执行任务1 线程池中的线程数:1,队列中等待执行任务数:0,已经执行完的任务数:7 线程池中的线程数:2,队列中等待执行任务数:0,已经执行完的任务数:7 正在执行任务2 线程池中的线程数:3,队列中等待执行任务数:0,已经执行完的任务数:7 线程池中的线程数:4,队列中等待执行任务数:0,已经执行完的任务数:7 正在执行任务3 线程池中的线程数:5,队列中等待执行任务数:0,已经执行完的任务数:7 线程池中的线程数:5,队列中等待执行任务数:1,已经执行完的任务数:7 线程池中的线程数:5,队列中等待执行任务数:2,已经执行完的任务数:7 线程池中的线程数:5,队列中等待执行任务数:3,已经执行完的任务数:7 线程池中的线程数:5,队列中等待执行任务数:4,已经执行完的任务数:7 线程池中的线程数:6,队列中等待执行任务数:4,已经执行完的任务数:7 正在执行任务4 线程池中的线程数:7,队列中等待执行任务数:4,已经执行完的任务数:7 正在执行任务5 正在执行任务10 Exception in thread "main" 正在执行任务11 java.util.concurrent.RejectedExecutionException: Task day08.MyRunable@42a57993 rejected from java.util.concurrent.ThreadPoolExecutor@75b84c92[Running, pool size = 7, active threads = 7, queued tasks = 4, completed tasks = 0] at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source) at day08.Test.main(Test.java:21) 任务11执行完毕! 任务3执行完毕! 任务2执行完毕! 正在执行任务6 任务1执行完毕! 正在执行任务9 任务10执行完毕! 任务4执行完毕! 任务5执行完毕! 正在执行任务7 正在执行任务8 任务9执行完毕! 任务8执行完毕! 任务7执行完毕! 任务6执行完毕! -
报错原因
-
因为12个任务,你只安排了7个线程去执行并且4个信息队列存储任务,第12个任务存储不下被销毁,容量不够,报错
-
解决办法:很简单,增加最大线程容量或者扩大队列
-
如
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 8, 300, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4)); -
结果
正在执行任务1 线程池中的线程数:1,队列中等待执行任务数:0,已经执行完的任务数:8 线程池中的线程数:2,队列中等待执行任务数:0,已经执行完的任务数:8 线程池中的线程数:3,队列中等待执行任务数:0,已经执行完的任务数:8 正在执行任务2 线程池中的线程数:4,队列中等待执行任务数:0,已经执行完的任务数:8 正在执行任务4 线程池中的线程数:5,队列中等待执行任务数:0,已经执行完的任务数:8 正在执行任务3 线程池中的线程数:5,队列中等待执行任务数:1,已经执行完的任务数:8 线程池中的线程数:5,队列中等待执行任务数:2,已经执行完的任务数:8 线程池中的线程数:5,队列中等待执行任务数:3,已经执行完的任务数:8 线程池中的线程数:5,队列中等待执行任务数:4,已经执行完的任务数:8 正在执行任务5 线程池中的线程数:6,队列中等待执行任务数:4,已经执行完的任务数:8 线程池中的线程数:7,队列中等待执行任务数:4,已经执行完的任务数:8 正在执行任务10 线程池中的线程数:8,队列中等待执行任务数:4,已经执行完的任务数:8 正在执行任务11 正在执行任务12 任务11执行完毕! 任务12执行完毕! 正在执行任务6 正在执行任务7 任务3执行完毕! 任务5执行完毕! 任务4执行完毕! 任务2执行完毕! 正在执行任务9 任务1执行完毕! 任务10执行完毕! 正在执行任务8 任务8执行完毕! 任务9执行完毕! 任务7执行完毕! 任务6执行完毕!
-
3195

被折叠的 条评论
为什么被折叠?



