异步与线程池
线程创建的方式
第一种
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 通过继承 thread 类,重写 run(),创建对象实例,调用start()方法启动线程
public class ThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
public static class MyThread extends Thread {
public void run() {
System.out.println("线程执行了" + Thread.currentThread().getId());
}
}
}第二种
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 实现 Runnable 接口,重写run(),创建对象实例,使用Thread线程启动线程
public class ThreadTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable).start();
}
public static class MyRunnable implements Runnable{
public void run() {
System.out.println("线程执行了" + Thread.currentThread().getId());
}
}
}第三种
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// 实现 Callable<T> 接口,重写call(),使用FurureTask对象启动线程
// Callable 它是异步执行,调用 get(),会阻塞等待,直到线程结束
public class ThreadTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> task = new FutureTask<>(new MyCallable());
new Thread(task).start();
Integer value = task.get();
System.out.println(value);
}
public static class MyCallable implements Callable<Integer> {
public Integer call() throws Exception {
System.out.println("线程执行了" + Thread.currentThread().getId());
Integer num = 10 / 2;
return num;
}
}
}第四种:**
线程池**1
2
3
4
5
6// 在高并发,多线程的程序开发中,持续的创建单个线程,很消耗资源
// 所以,在一个系统中,会维护一个线程池,有线程任务交给线程池去执行.
public static ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(new runnable());
总结:
第一种和第二种创建线程,不能得到返回值,
第三种可以获取返回值
第一,二,三种方式,均不能控制资源
第四种可以控制资源
线程池
线程池7大参数解析
1 | // 构造一 |
- corePoolSize :线程池核心线程数量,即使是线程处于空闲状态也不会销毁,除非设置了allowCoreThreadTimeOut。也是最小线程数量
- maximumPoolSize :线程池最大线程数量
- 一个任务被提交到线程池后,首先会找有没有空闲的存活的线程,如果有则直接将任务交给空闲的线程来执行;如果没有则会缓存到工作队列种,如果工作队列满了,才会创建一个新的线程,然后从工作队列的头部取出一个任务交由新线程来执行。而刚刚提交的任务放入队列的尾部。线程池不会无限制的去创建新线程,他会有一个最大线程数量的限制,这个数量就是有 maximumPoolSize 来指定的。
- keepAliveTime :当线程数大于核心时,这是多余的空闲线程在终止前等待新任务的最长时间。
- unit :keepAliveTime 参数的时间单位
- workQueue:阻塞队列,用来存储等待执行的任务。如果当前对线程的需求超过了 corePoolSize 指定的大小,就会放在这里等待空闲线程执行。
- threadFactory:线程的创建工厂
- handler:如果队列满了,按照指定的拒绝策略,拒绝执行任务。
工作流程
- 线程池创建,准备好 core 数量的核心线程,准备接受任务
- 新的任务进来,用 core 准备好的空闲线程执行。
- core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队列获取任务执行
- 阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
- max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。最终保持到 core 大小
- 如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策略进行处理
- 所有的线程创建都是由指定的 factory 创建的
案例
一个线程池 core 7; max 20 ,queue:50,100 并发进来怎么分配的
- 先使用核心线程7个执行任务,多余任务进入队列,再开13个线程继续执行,最大开到20个.剩余30个会被默认策略拒绝
常见的4种线程池
- newCachedThreadPool
- 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
- newFixedThreadPool
- 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
- newScheduledThreadPool
- 创建一个定长线程池,支持定时及周期性任务执行
- newSingleThreadExecutor
- 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
开发中线程池的优势
- 降低资源的消耗
- 通过重复利用已经创建好的线程降低线程的创建和销毁带来的消耗
- 提高响应速度
- 因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
- 提高线程的可管理性
- 线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销.无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配