1. 快速入门
/**
* 线程池快速入门
*
*/
class Student{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class ThreadPool01 {
//需求:登记学生的名字、年龄信息
public static void main(String[] args) throws InterruptedException {
List<Student> list = new ArrayList<>();
for(int i =0;i<100;i++){
list.add(new Student("小赵"+i,10));
}
long start = System.currentTimeMillis();
//处理学生信息
list.forEach(student -> {
//TODO 登记学生信息
ThreadHelper.sleep(10);
System.out.println(student);
});
System.out.println("单线程处理时间:"+(System.currentTimeMillis()-start));
//创建5个线程的线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
CountDownLatch countDownLatch = new CountDownLatch(100);
for (Student student : list) {
//execute 提交任务,接收一个Runnable类型的对象,内部调用其run方法,执行任务
pool.execute(() -> {
ThreadHelper.sleep(10);
System.out.println(student);
countDownLatch.countDown();
});
}
countDownLatch.await();
System.out.println("线程池处理时间:"+(System.currentTimeMillis()-start));
pool.shutdown();//关闭线程池
}
}
2. 线程池介绍
1. 几种常见的线程池
/**
* 常用的几种线程池
*/
class Task implements Runnable{
int taksNum;//任务编号
public Task(int taksNum) {
this.taksNum = taksNum;
}
@Override
public void run() {
ThreadHelper.sleep(10);
String name = Thread.currentThread().getName();
System.out.println("任务"+taksNum+"被"+name+"执行。。。。。。");
}
}
public class ThreadPool02 {
public static void main(String[] args) throws InterruptedException {
// 1. 创建固定线程数的线程池,可控制线程最大并发数,超出的线程数量的任务,会在队列中等待
ExecutorService pool = Executors.newFixedThreadPool(5);
for (int i = 1; i < 11; i++) {
pool.execute(new Task(i));
}
ThreadHelper.sleep(5000L);//主线程等待
pool.shutdown();
// 2. 创建单个线程的线程池,保证所有任务按照指定顺序,如果处理任务的过程中报错,会创建一个新的线程
// ExecutorService pool = Executors.newSingleThreadExecutor();
// for (int i = 1; i < 11; i++) {
// pool.execute(new Task(i));
// }
// pool.shutdown();
// 3. 创建一个不限制数量的线程池,如果所有线程都在工作且又有新的任务,会创建新的线程,如果有线程空闲,就复用,线程空闲60s回收
// 比较适合处理执行时间比较短的任务
// ExecutorService pool = Executors.newCachedThreadPool();
// for (int i = 1; i < 11; i++) {
// pool.execute(new Task(i));
// }
// ThreadHelper.sleep(6000L);//主线程等待
// pool.shutdown();
//
// // 4. 创建拥有固定线程数量的定时线程任务的线程池
// ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
// System.out.println("当前时间:" + System.currentTimeMillis());
// for (int i = 1; i < 2; i++) {
// // a. 延期执行
pool.schedule(new Task(i),3, TimeUnit.SECONDS);// 延期3秒执行
//
// // b. 周期执行
// pool.scheduleAtFixedRate(new Task(i),3,1, TimeUnit.SECONDS);// 延期3秒钟后,每隔1秒执行一次任务
pool.scheduleWithFixedDelay(new Task(i),3,1, TimeUnit.SECONDS);// 延期3秒钟执行,上次任务执行结束1秒后,本次任务开始执行
// }
// ThreadHelper.sleep(60000L);//主线程等待
// pool.shutdown();
}
面试题:单个线程池和固定数量的设置为1,有什么区别?
-
- newFixedThreadPool返回的是 ThreadPoolExecutor 对象,newSingleThreadExecutor返回的是 FinalizableDelegatedExecutorService,对 ThreadPoolExecutor 做了一层封装
- FinalizableDelegatedExecutorService相对ThreadPoolExecutor来说,提供的方法更少,保证了一旦被创建就永远只能为单线程
1. 主要参数
corePoolSize | 核心线程数量 |
maximumPoolSize | 最大线程数量 |
keepAliveTime | 没有任务时,线程最多存活的时间,超过这个时间就被终止了。 默认情况下,只有在线程池线程数量超出corePoolSize,才会把超时的空闲线程给停止掉, 否则保持线程池中有 corePoolSize 个线程 |
unit | 参数keepAliveTime的时间单位,就是 TimeUnit类当中的几个属性 |
workQueue | 用来存储待执行任务的队列,有以下几种:
|
threadFactory | 线程工厂,用来创建线程。 |
Handler | 拒绝策略,有以下几种:
|
2. 执行流程、原理
开始时,每来一个任务创建一个线程执行,直到线程数达到核心线程数,再来任务就放到队列中,如果队列满了,继续创建线程执行,直到线程数达到最大线程数,如果还有任务,就执行拒绝策略。
当没有任务时且线程数量大于核心线程数时,多余的线程的空闲时间超过了keepAliveTime,那么这个线程就要被销毁了,直到线程数量达到核心线程数
注意:我们传进去的任务对象是Runnable类型的,但最终是以 对象.run() 的方式运行的
3. 主要方法
void execute(Runnable task):接收Runnable类型的任务对象,没有返回值
如果异常,直接抛出
Future<?> submit(Runnable task):接收Runnable类型的任务对象,返回Future对象
如果异常,调用future对象的get方法时才会报错
<T> Future<T> submit(Runnable task, T result) :如果成功,调用get方法能够得到传进去的result对象
<T> Future<T> submit(Callable<T> task):接收Callable类型的任务对象,如果成功,调用get方法能够得到返回值
shutdown:让线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕
shutdownNow:让线程池的状态立刻变成STOP状态,并试图停止所有正在执行的线程,不再处理还在池队列中等待的任务,当然,它会返回那些未执行的任务。
3. 设置合理的线程数
/**
* 设置合理的线程数量
* 1.计算密集型,又称CPU密集型,比如:数据都在内存中,需要读数据进行处理、运算、组装,线程不会阻塞,这时候压榨CPU性能,
* CPU核数+1
*
* 2.IO密集型,线程阻塞或等待,比如:数据库读写,磁盘文件读写
* CPU核数 * 2
* CPU核数 / (1 – 阻塞系数)(0.8~0.9之间)
*
* 通用公式:(1 + 线程等待时间/线程CPU时间)*CPU利用率*CPU数量
*
* 3. 混合型,先考虑需不需要拆分,然后在挨个测试
*
* 4. 监控系统资源,动态修改(传说GO语言可以实现)
*
* 注意:最终数量都应该通过实际不断测试得出,各种公式只是参考
*
*/
public class ThreadPool05 {
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());//输出可用的cup个数
}
}