1、初始化线程的 4 种方式
1)继承 Thread
package com.search.thread;
import java.util.concurrent.*;
public class ThreadTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main......start.....");
Thread thread = new Thread01();
thread.start();
System.out.println("main......end.....");
}
public static class Thread01 extends Thread {
@Override
public void run() {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
}
}
}
2)实现 Runnable 接口
package com.search.thread;
import java.util.concurrent.*;
public class ThreadTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main......start.....");
Runable01 runable01 = new Runable01();
new Thread(runable01).start();
System.out.println("main......end.....");
}
public static class Runable01 implements Runnable {
@Override
public void run() {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
}
}
}
3)实现 Callable 接口 + FutureTask (可以拿到返回结果,可以处理异常)
package com.search.thread;
import java.util.concurrent.*;
public class ThreadTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main......start.....");
FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
new Thread(futureTask).start();
//阻塞等待整个线程执行完成,获取返回结果
System.out.println(futureTask.get());
System.out.println("main......end.....");
}
public static class Callable01 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
return i;
}
}
}
4)线程池
package com.search.thread;
import java.util.concurrent.*;
/**
给线程池直接提交任务。
1、创建:
service.execute(new Runable01());
1)、Executors
2) 、new ThreadPoolExecutor
Future:可以获取到异步结果
我们以后在业务代码里面,以上三种启动线程的方式都不用。将所有的多线程异步任务都交给线程池执行。
当前系统中池只有一两个,每个异步任务,提交给线程池让他自己去执行就行。
**/
public class ThreadTest {
public static ExecutorService executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main......start.....");
// CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
// System.out.println("当前线程:" + Thread.currentThread().getId());
// int i = 10 / 2;
// System.out.println("运行结果:" + i);
// }, executor);
/**
* 方法完成后的处理
*/
// CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// System.out.println("当前线程:" + Thread.currentThread().getId());
// int i = 10 / 0;
// System.out.println("运行结果:" + i);
// return i;
// }, executor).whenComplete((res,exception) -> {
// //虽然能得到异常信息,但是没法修改返回数据
// System.out.println("异步任务成功完成了...结果是:" + res + "异常是:" + exception);
// }).exceptionally(throwable -> {
// //可以感知异常,同时返回默认值
// return 10;
// });
/**
* 方法执行完后端处理
*/
// CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// System.out.println("当前线程:" + Thread.currentThread().getId());
// int i = 10 / 2;
// System.out.println("运行结果:" + i);
// return i;
// }, executor).handle((result,thr) -> {
// if (result != null) {
// return result * 2;
// }
// if (thr != null) {
// System.out.println("异步任务成功完成了...结果是:" + result + "异常是:" + thr);
// return 0;
// }
// return 0;
// });
/**
* 线程串行化
* 1、thenRunL:不能获取上一步的执行结果
* 2、thenAcceptAsync:能接受上一步结果,但是无返回值
* 3、thenApplyAsync:能接受上一步结果,有返回值
*
*/
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
return i;
}, executor).thenApplyAsync(res -> {
System.out.println("任务2启动了..." + res);
return "Hello" + res;
}, executor);
System.out.println("main......end....." + future.get());
}
private static void threadPool() {
ExecutorService threadPool = new ThreadPoolExecutor(
200,
10,
10L,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(10000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
//定时任务的线程池
ExecutorService service = Executors.newScheduledThreadPool(2);
}
}
方式 1 和方式 2:主进程无法获取线程的运算结果。不适合当前场景
方式 3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致服务器资源耗尽。
方式 4:通过如下两种方式初始化线程池
Executors.newFiexedThreadPool(3);
//或者
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit, workQueue, threadFactory, handler);
通过线程池性能稳定,也可以获取执行结果,并捕获异常。但是,在业务复杂情况下,一个异步调用可能会依赖于另一个异步调用的执行结果。
二、线程池7大参数
七大参数:
corePoolSize:[5] 核心线程数[一直存在,除非 (allowCoreThreadTime0ut) ]; 线程池创建好以后就准备就绪的线程数量,就等待来接受异步任务去执行。
5个 Thread thread = new Thread(); thread,start();
maximumPoolsize:[200] 最大线程数量; 控制资源
keepAliveTime:存活时间。如果当前的线程数量大于core数量。释放空闲的线程(maximumPoolSize-corePoolSize)。只要线程空闲大于指定的keepAliveTime;
unit:时间单位
BlockingQueue<Runnable> workQueue:阻塞队列。如果任务有很多,就会将目前多的任务放在队列里面,只要有线程空闲,就会去队列里面取出新的任务继续执行。
threadFactory:线程的创建工厂。
RejectedExecutionHandler handler;如果队列满了,按照我们指定的拒绝策略拒绝执行任务
三、线程池运行流程
1、线程池创建,准备好 core 数量的核心线程,准备接受任务。
2、新的任务进来,用 core 准备好的空闲线程执行。
(1) 、core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队列获取任务执行。
(2)、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量
(3)、max都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。最终保持到 core 大小。
(4)、如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策略进行处理。
3、所有的线程创建都是由指定的 factory 创建的。
四、面试题
一个线程池 core 7; max 2 ,queue: 5,如果100并发进来是怎么分配的?
7个会立即得到执行,50个会进入队列,再开13个进行执行。剩下的30个就使用拒绝策略.如果不想抛弃还要执行。CallerRunsPolicy;
五、常见的 4 种线程池
1.newCachedThreadPool(core是0,所有都可回收):创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若 无可回收,则新建线程。
2.newFixedThreadPool(固定大小,core=max;都不可回收):创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3. newScheduledThreadPool(定时任务的线程池):创建一个定长线程池,支持定时及周期性任务执行。
4.newSingleThreadExecutor(单线程的线程池,后台从队列里面获取任务,挨个执行):创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
六、开发中为什么使用线程池
1.降低资源的消耗:通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
2.提高响应速度:因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
3.提高线程的可管理性:线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
七、线程池在项目中的实际应用
1.CompletableFuture 异步编排
业务场景:
查询商品详情页的逻辑比较复杂,有些数据还需要远程调用,必然需要花费更多的时间。
假如商品详情页的每个查询,需要如下标注的时间才能完成。那么,用户需要 5.5s 后才能看到商品详情页的内容。很显然是不能接受的。
如果有多个线程同时完成这 6 步操作,也许只需要 1.5s 即可完成响应。