线程的创建与使用
创建线程池的集中方式
- 通过Executors工厂类创建线程池:
- 通过ThreadPoolExecutor构造函数创建线程池:
@Configuration
public class ExecutorServiceConfig {
@Bean(name = "datasourceExecutorService")
public ExecutorService datasourceExecutorService() {
/*
// 1. 通过Executors工厂类创建线程池:
// 创建一个可缓存线程池,线程数根据任务数量动态调整
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 创建一个固定大小的线程池,最多同时执行5个线程
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
// 创建一个定时执行任务的线程池,核心线程数为3
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
// 创建一个单线程的线程池,保证所有任务按照指定顺序执行
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
// 2. 通过ThreadPoolExecutor构造函数创建线程池:
// 创建一个固定大小的线程池
ExecutorService fixedThreadPool1 = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
// 创建一个单线程的线程池
ExecutorService singleThreadPool1 = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
// 创建一个可缓存的线程池
ExecutorService cachedThreadPool1 = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
// 创建一个定时执行任务的线程池
ScheduledExecutorService scheduledThreadPool1 = new ScheduledThreadPoolExecutor(3);
*/
return new ThreadPoolExecutor(
//线程池中核心线程数的最大值
Runtime.getRuntime().availableProcessors(),
//线程池中能拥有最多线程数(阻塞队列满了才用到)
(int) (Runtime.getRuntime().availableProcessors() * 1.5),
//空闲线程的存活时间
60, java.util.concurrent.TimeUnit.SECONDS,
//用于缓存任务的阻塞队列
new LinkedBlockingQueue<>(50 * 1024),
new ThreadFactoryBuilder().setNameFormat("thread-pool-%d").build(),
//在任务被拒绝添加后,会由主线程去执行被拒绝的任务,当线程池有空闲时,才继续执行其他的任务。所以此策略可能会阻塞主线程
new ThreadPoolExecutor.CallerRunsPolicy());
}
}
创建线程的方式
- 通过实现
Runnable接口 - 通过实现
Callable接口 - 通过继承
Thread类 - 通过线程池创建
1. 通过实现Runnable接口创建线程
@Test
public void testRunnable() {
List<Integer> runnableTestResult = runnableTest();
System.out.println("runnableTestResult = " + runnableTestResult);
}
/**
* 通过实现 Runnable接口创建线程
*/
public List<Integer> runnableTest() {
List<Integer> xList = Lists.newArrayList(1, 2, 3, 4, 5);
List<Integer> yList = Lists.newArrayList(1, 2, 3, 4, 5);
// 线程安全的队列实现,适用于高并发环境下的队列操作。
ConcurrentLinkedQueue<Integer> result = new ConcurrentLinkedQueue<>();
List<Future<?>> futureList = new ArrayList<>();
for (int i = 0; i < xList.size(); i++) {
Integer x = xList.get(i);
Integer y = yList.get(i);
futureList.add(
// 提交任务给线程池
executorService.submit(new Runnable() {
@Override
public void run() {
// todo 工作逻辑处理
String threadName = Thread.currentThread().getName();
long threadId = Thread.currentThread().getId();
log.info("当前线程ID:[{}],当前线程名称:[{}]", threadId, threadName);
result.add((x != null ? x : 0) + (y != null ? y : 0));
}
})
);
}
for (Future<?> future : futureList) {
try {
future.get();
} catch (Exception e) {
log.error("获取线程池结果出现异常", e);
Thread.currentThread().interrupt();
}
}
executorService.shutdown();
return new ArrayList<>(result);
}
2. 通过实现Callable接口创建线程
- 创建
CallableTest类实现Callable接口 - 重写
call()方法,编写业务逻辑
@Slf4j
public class CallableTest implements Callable<Integer> {
private final Integer x;
private final Integer y;
public CallableTest(Integer x,Integer y) {
this.x = x;
this.y = y;
}
@Override
public Integer call() throws Exception {
return call( x, y);
}
public Integer call(Integer x,Integer y) {
// todo 业务逻辑
String threadName = Thread.currentThread().getName();
long threadId = Thread.currentThread().getId();
log.info("当前线程ID:[{}],当前线程名称:[{}]",threadId,threadName);
return (x != null ? x : 0) + (y != null ? y : 0);
}
}
[使用示例]
@Test
public void testCallable() {
List<Integer> testCallableResult = callableTest();
System.out.println("testCallableResult = " + testCallableResult);
}
/**
* 通过实现Callable接口创建线程
*/
public List<Integer> callableTest() {
List<Integer> xList = Lists.newArrayList(1, 2, 3, 4, 5);
List<Integer> yList = Lists.newArrayList(1, 2, 3, 4, 5);
List<Future<Integer>> futureList = new ArrayList<>();
for (int i = 0; i < xList.size(); i++) {
futureList.add(executorService.submit(new CallableTest(xList.get(i), yList.get(i))));
}
List<Integer> result = new ArrayList<>();
for (Future<Integer> future : futureList) {
try {
result.add(future.get(30, java.util.concurrent.TimeUnit.SECONDS));
} catch (Exception e) {
log.error("获取线程池结果出现异常", e);
result.add(null);
Thread.currentThread().interrupt();
}
}
executorService.shutdown();
return result;
}
3. 通过继承Thread类
- 创建
MyThreadTest类继承Thread - 重写
run()方法,编写业务逻辑
@Slf4j
public class MyThreadTest extends Thread{
@Override
public void run() {
// todo 业务处理
String threadName = Thread.currentThread().getName();
long threadId = Thread.currentThread().getId();
log.info("当前线程ID:[{}],当前线程名称:[{}]",threadId,threadName);
System.out.println("threadId = " + threadId);
}
}
[使用示例]
/**
* 通过继承 Thread 类创建线程类
*/
@Test
public void testThread() {
MyThreadTest myThreadTest = new MyThreadTest();
myThreadTest.start();
}
4. 通过线程池创建
/**
* 通过线程池创建线程
*/
@Test
public void testThreadPoll() {
List<Integer> threadPollTestResult = threadPollTest();
System.out.println("threadPollTestResult = " + threadPollTestResult);
}
public List<Integer> threadPollTest(){
// 创建固定大小为 3 的线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
List<Integer> xList = Lists.newArrayList(1, 2, 3, 4, 5);
List<Integer> yList = Lists.newArrayList(1, 2, 3, 4, 5);
// 线程安全的队列实现,适用于高并发环境下的队列操作。
ConcurrentLinkedQueue<Integer> result = new ConcurrentLinkedQueue<>();
List<Future<?>> futureList = new ArrayList<>();
for (int i = 0; i < xList.size(); i++) {
Integer x = xList.get(i);
Integer y = yList.get(i);
// 提交任务给线程池
futureList.add(executor.submit(() -> {
// todo 工作逻辑处理
String threadName = Thread.currentThread().getName();
long threadId = Thread.currentThread().getId();
log.info("当前线程ID:[{}],当前线程名称:[{}]",threadId,threadName);
result.add((x != null ? x : 0) + (y != null ? y : 0));
}));
}
for (Future<?> future : futureList) {
try {
future.get();
} catch (Exception e) {
log.error("获取线程池结果出现异常", e);
Thread.currentThread().interrupt();
}
}
executor.shutdown();
return new ArrayList<>(result);
}
线程池创建的使用区别
实现Callable接口和实现Runnable接口创建的线程之间区别
1. 返回结果
实现Runnable接口的线程任务不返回结果,`run()`方法没有返回值。
实现Callable接口的线程任务可以返回结果,任务的`call()`方法可以返回计算结果。
2. 抛出异常
实现Runnable接口的线程任务的`run()`方法无法抛出已检查异常(checked exception),只能在方法内部捕获处理。
实现Callable接口的线程任务的`call()`方法可以抛出已检查异常,允许在方法声明中抛出异常,调用者需要处理异常。
3. 返回值处理
Runnable接口的线程任务无法获取任务执行的结果,因为`run()`方法没有返回值。
Callable接口的线程任务可以通过`Future`对象获取线程执行结果,可以进一步对结果进行处理。