创建线程的方式
继承Thread类(extends)
实现Runnable接口(implements)
实现Callable接口+Future Task(可以拿到返回结果,可处理异常)
线程池:Executors/ThreadPoolExecutor
线程池工作流程
- 线程池创建准备好corePoolSize大小的核心线程准备
- 任务来了先试用核心线程
- core满了,将再进来的任务放到阻塞队列中等候
- 阻塞队列满了,直接开启新的线程(不超过max数量)
- 非核心线程会在keepAliveTime后自动销毁
- 超过max数量的任务执行拒绝策略(abortPolicy、discardPolicy、discardOldPolicy、callerRunsPolicy)
- 所有的线程创建都是在线程工厂中完成
Executors()
newFixedThreadPool();固定大小
newCachedThreadPool();无限大小
newSingleThreadExecutor();单线程
scheduledThreadExecutor();定长定时
*不建议使用(有内存泄漏风险)
ThreadPoolExcutor()
参数
int corePoolSize,
int maximumPoolSize
long keepAliveTime
TimeUnit unit
BlockingQueue workQueue
ThreadFactory threadFactory
RejectedExecutionHandler handler(策略模式)context+RejectedExecutionHandlerInterface+具体拒绝实现
代码实现
package com.example.demo.controller;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.*;
/**
* @author Adam
* @version 1.0
* @description 线程的创建
* @date 2022/5/14
*/
public class TestThread {
public static void main(String[] args) throws Exception {
// 1.继承Thread类
Thread0 thread0 = new Thread0();
thread0.run();
// 2.实现Runnable接口
Runnable0 runnable0 = new Runnable0();
runnable0.run();
// 3.实现Callable接口
Callable0 callable0 = new Callable0();
callable0.call();
// 4.线程池
// 4.1Executors
ExecutorService executorService = Executors.newFixedThreadPool(1);
System.out.println("4.1Executors创建线程池");
executorService.submit(runnable0);
executorService.shutdown();
// 4.2ThreadPoolExecutor
/**
* int corePoolSize,
* int maximumPoolSize,
* long keepAliveTime,
* TimeUnit unit,
* BlockingQueue<Runnable> workQueue,
* ThreadFactory threadFactory,
* RejectedExecutionHandler handler
*/
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
20,
100,
TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>(10)
);
//LinkedBlockingDeque和LinkedBlockingQueue的区别:
//线程安全以及阻塞等待的实现基本没有区别,两个阻塞队列基本可以通用(LinkedBlockingDueue用作栈时除外)。
// 不同:一个是底层数据结构的细微区别,LinkedBlockingQueue是单向链表,而LinkedBlockingDueue则是双向链表;
// S另一个是重入锁的使用有些区别,LinkedBlockingDueue不论出入队都使用的是同一个锁对象,而LinkedBlockingQueue的出入队锁是分开的。
threadPoolExecutor.execute(() -> {
System.out.println("4.2ThreadPoolExecutor创建线程池");
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
System.out.println("当前线程总数为:" + bean.getThreadCount());
});
threadPoolExecutor.shutdown();
}
/**
* 继承Thread类
*/
private static class Thread0 extends Thread {
@Override
public void run() {
System.out.println("1.继承Thread类");
System.out.println("当前线程ID:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
System.out.println("------------------------------------");
}
}
/**
* 实现Runnable接口
*/
private static class Runnable0 implements Runnable {
@Override
public void run() {
System.out.println("2.实现Runnable接口");
System.out.println("当前线程ID:" + Thread.currentThread().getId());
int i = 10 / 5;
System.out.println("运行结果:" + i);
System.out.println("------------------------------------");
}
}
/**
* 实现Callable接口
*/
private static class Callable0 implements Callable {
@Override
public Integer call() throws Exception {
System.out.println("3.实现Callable接口");
System.out.println("当前线程ID:" + Thread.currentThread().getId());
int i = 20 / 5;
System.out.println("运行结果:" + i);
System.out.println("------------------------------------");
return i;
}
}
}
各方式优缺点对比
实现方式 | 优点 | 缺点 |
---|---|---|
继承Thread类 | 编写简单,使用 this 即可获得当前线程 | 不能再继承其他父类 |
实现Runnable接口 | 可继承其他类 | 不能处理异常和获取处理结果 |
实现Callable接口+Future Task | 可以继承其他类,可拿到返回结果,可处理常 | 异步编排实现复杂 |
Executors创建线程池 | 减少创建销毁线程开销 | 内存泄漏隐患,易OOM |
ThreadPoolExecutor创建线程池 | 减少创建销毁线程开销 | 需要手动关闭线程池避免资源浪费 |