线程池在Java中是通过java.util.concurrent.ExecutorService接口及其实现类来使用的。以下是使用线程池的基本步骤和示例:
1. 选择或创建线程池
Java提供了几种标准的线程池实现,如Executors类中的工厂方法可以方便地创建固定大小的线程池、单线程的线程池等。你也可以根据需要使用ThreadPoolExecutor
自定义线程池。
- 使用
Executors
工厂方法创建的线程池通常适用于临时任务。对于需要长期运行的服务,建议使用ThreadPoolExecutor
来自定义线程池的配置。
//1.创建一个缓存可重用的线程池: 此线程池的特点是线程数可以动态增长,以合理的数量处理负载。
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//2.创建一个固定大小可重用的线程池: 新提交的任务将放入队列中等待。此线程池的特点是队列容量无限,可以缓冲尽量多的任务。
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
//3.创建一个单线程的线程池: 此线程池的特点是任务按照指定顺序(FIFO, LIFO, 优先级)执行
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
//4.创建一个定时单线程线程池
ScheduledExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
//5.创建一个定时执行任务的线程池: 是大小无限的线程池,用于执行定时任务,或者在指定的延迟后执行任务
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
//6.创建一个带有并行级别的线程池: 是Java 8中引入的,此线程池的特点是能够充分利用多核处理器,适用于大量的小任务。
ExecutorService workStealingPool = Executors.newWorkStealingPool();
2. 提交任务到线程池
使用execute()方法提交不需要返回值的任务,或者提交`Callable`任务并使用submit()方法来获取一个`Future`对象。
// 提交Runnable任务
executor.execute(new Runnable() {
@Override
public void run() {
// 执行任务的代码
}
});
// 提交Callable任务并获取Future
Future<String> future = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
// 执行任务的代码
return "Result";
}
});
3. 处理任务结果
如果你使用submit()方法提交了`Callable`任务,你可以通过`Future`对象来获取任务执行的结果。
// 获取任务结果
String result = future.get(); // 阻塞直到任务完成
4. 管理线程池
在任务提交完毕后,你可能需要管理线程池,比如关闭线程池,等待所有任务执行完成等。
// 关闭线程池,不再接受新任务
executor.shutdown();
// 等待所有任务完成
while (!executor.isTerminated()) {
// 可以进行其他操作或者等待
}
// 优雅地关闭线程池
executor.shutdownNow(); // 尝试立即关闭线程池,停止正在执行的任务
5. 自定义线程池
如果需要更精细的控制,可以创建自定义的线程池,通过ThreadPoolExecutor类来设置核心线程数、最大线程数、存活时间、工作队列和拒绝策略等。
int corePoolSize = 5;//核心线程数
int maximumPoolSize = 10;//最大线程数
long keepAliveTime = 60;//空闲线程存活时间
TimeUnit keepAliveTimeUnit = TimeUnit.SECONDS;//时间单位
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100);//阻塞队列
ThreadFactory threadFactory = Executors.defaultThreadFactory();//线程工厂
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();//拒绝策略
//自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
keepAliveTimeUnit,
workQueue,
threadFactory,
handler
);
使用自定义的ThreadPoolExecutor可以更细致地控制线程池的行为,以适应不同的应用场景和性能要求。
在实际开发中,合理使用线程池可以有效地提高应用程序的并发处理能力和资源利用率。
package com.xx.xx;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
//创建线程的4种方式代码示例:
public class CreateThreads {
//1.继承Thread类并虫子额run()方法
public class MyThread1 extends Thread{
public void run(){
System.out.println("my Thread1 is start");
}
}
MyThread1 mt1 = new MyThread1();
mt1.start();
//2.实现Runable接口并重写run()
public class MyThread2 implements Runnable{
public void run() {
System.out.println("my Thread2 is start");
}
}
//启动 MyThread,需要首先实例化一个 Thread,并传入自己的 MyThread 实例:
MyThread2 mt2 = new MyThread2();
Thread thread = new Thread(mt2);
thread.start();
//事实上,当传入一个 Runnable target 参数给 Thread 后,Thread 的 run()方法就会调用
target.run()
public void run() {
if (target != null) {
target.run();
}
}
// 3.利用线程池
ExecutorService threadPool = Executors.newFixedThreadPool(10);
while(true) {
threadPool.execute(new Runnable() { // 提交多个线程任务,并执行
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running ..");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
//实现Callable接口并重写call()创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
// 创建多个有返回值的任务
List<Future> list = new ArrayList<Future>();
for (int i = 0; i < taskSize; i++) {
Callable c = new MyCallable(i + " ");
// 执行任务并获取 Future 对象
Future f = pool.submit(c);
list.add(f);
}
// 关闭线程池
pool.shutdown();
// 获取所有并发任务的运行结果
for (Future f : list) {
// 从 Future 对象上获取任务的返回值,并输出到控制台
System.out.println("res:" + f.get().toString());
}
}