初始化线程的4种方式
1. 继承Thread类
1. 继承Thread类 2. 重写run( )方法 3. new一个对象之后通过.start( ) 开启一个线程
Thread01 thread01 = new Thread01();
thread01.start(); //启动线程
public class ThreadTest {
public static void main(String[] args) {
System.out.println("main......start...");
Thread01 thread01 = new Thread01();
thread01.start();
System.out.println("main......end...");
}
public static class Thread01 extends Thread{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getName());
Integer i=10/2;
System.out.println("运行结果:"+i);
}
}
}
2. 实现 Runnable 接口
1. 实现Runable接口 2. 重写run( ) 方法
3. new一个对象之后作为参数Thread类构造函数的参数 调用start( ) 方法
实现 Runnable 接口
Runable01 runable01 = new Runable01();
new Thread(runable01).start();
public class ThreadTest {
public static void main(String[] args) {
System.out.println("main......start...");
Thread02 thread02 = new Thread02();
new Thread(thread02).start();
System.out.println("main......end...");
}
public static class Thread02 implements Runnable{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getName());
Integer i=12/2;
System.out.println("运行结果:"+i);
}
}
}
3. 实现 Callable 接口 + FutureTask (可以拿到返回结果, 可以处理异常)
1. 实现Callable接口 2. 重写Call()方法 3. 通过start()方法开启一个线程
实现 Callable 接口 + FutureTask (可以拿到返回结果, 可以处理异常)
Callable01 callable01 = new Callable01();
FutureTask<Integer> futureTask = new FutureTask<>(callable01);
new Thread(futureTask).start();
//阻塞等待整个线程执行完成。获取返回结果
Integer integer = futureTask.get();
public class ThreadTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask futureTask = new FutureTask<>(new Thread03());
new Thread(futureTask).start();
Integer i = (Integer) futureTask.get();
System.out.println("main......end..."+i);
}
public static class Thread03 implements Callable {
@Override
public Object call() throws Exception {
System.out.println("当前线程:"+Thread.currentThread().getName());
Integer i=14/2;
System.out.println("运行结果:"+i);
return i;
}
}
}
4. 线程池
给线程池直接提交任务
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new Runable01()); //将任务提交成线程池 execute没有返回值 submit有返回值
4.1 创建线程池的七大参数
1. corePoolSize:核心线程数,在队列未达到容量时,可以运行的最大线程数量
2. maximumPoolSize:最大线程数量,控制资源
3. keepAliveime: 【maximumPoolSize-corePoolSize 超过空闲时间释放线程】
4. TimeUnitunit:时间单位
5. workQueue: 阻塞队列,只要有线程空闲,就会去队列取出新的任务执行
6. threadFactory:线程的创建工厂
7.RejectedExecutionHandler handler:拒绝策略
参数之间的关系
先创建核心线程运行任务
如果核心线程数已经用满了,则将任务放入阻塞队列进行等待
当阻塞队列满了之后创建新的线程,最多创建到设置的最大线程数maximumPoolSize个
如果当前已经是最大线程数了,并且阻塞队列满了之后,就要触发拒绝策略来丢弃任务
运行流程
运行流程:
1、 线程池创建, 准备好 core 数量的核心线程, 准备接受任务
2、 新的任务进来, 用 core 准备好的空闲线程执行。
(1) 、 core 满了, 就将再进来的任务放入阻塞队列中。 空闲的 core 就会自己去阻塞队列获取任务执行
(2) 、 阻塞队列满了, 就直接开新线程执行, 最大只能开到 max 指定的数量
(3) 、 max 都执行好了。 Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自动销毁。 最终保持到 core 大小
(4) 、 如果线程数开到了 max 的数量, 还有新任务进来, 就会使用 reject 指定的拒绝略进行处理
4种常见的拒绝策略
如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任务时
1.ThreadPoolExecutor.AbortPolicy
丢弃新任务,并且抛出异常 Discard2.
ThreadPoolExecutor.DiscardPolicy
不处理新任务,直接丢弃掉
3.ThreadPoolExecutor.DiscardOldestPolicy
此策略将丢弃最早的未处理的任务请求。4.
ThreadPoolExecutor.CallerRunsPolicy
调用执行自己的线程运行任务
4.2 初始化线程池(方式一)
通过 Executor 框架的工具类 Executors 来创建
创建一个固定类型的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
常见的4种线程池
FixedThreadPool
核心线程数 = 最大线程数
任务队列最大长度为
Integer.MAX_VALUE
,可能堆积大量的请求,从而导致 OOM创建一个定长线程池, 可控制线程最大并发数, 超出的线程会在队列中等待
CachedThreadPool 核心线程数是0
来一个任务创建一个线程 允许创建的线程数量为
Integer.MAX_VALUE
该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
SingleThreadPool 核心线程数 = 最大线程数 = 1
创建一个单线程化的线程池, 它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
newScheduledThreadPool
该方法返回一个用来在给定的延迟后运行任务或者定期执行任务的线程池
初始化线程池(方式二)
自定义线程池: 通过ThreadPoolExecutor
构造函数来创建(推荐)
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
200,
10,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>( 100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
使用线程池的好处
1、降低资源的消耗【减少创建销毁线程的开销】
通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
2、提高响应速度【控制线程个数】
因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
3、提高线程的可管理性【例如系统中可以创建两个线程池,核心线程池、非核心线程池【短信等】,关闭非核心线程池释放内存资源】
线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
异步编排 CompletableFuture
在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture, 提供了非常强大的Future 的扩展功能, 可以帮助我们简化异步编程的复杂性, 提供了函数式编程的能力, 可以通过回调的方式处理计算结果, 并且提供了转换和组合 CompletableFuture 的方法。CompletableFuture 类实现了 Future 接口, 所以你还是可以像以前一样通过get方法阻塞或者轮询的方式获得结果, 但是这种方式不推荐使用。