文章目录
引言
在多线程编程中,线程池是一种非常重要的技术,它可以帮助我们更高效地管理和复用线程资源。Java提供了丰富的线程池实现,通过ExecutorService接口及其子类,我们可以轻松地创建和管理线程池。本文将详细介绍Java线程池的几种常见类型,并给出示例代码和常见错误案例。
Java线程池类型
1. FixedThreadPool
特点:
固定大小的线程池。
创建的线程池中的线程数量固定,超出的线程将在队列中等待。
适用于负载较重且任务数量较多的场景。
示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* FixedThreadPoolExample
* @author senfel
* @version 1.0
* @date 2024/10/24 11:17
*/
public class FixedThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
2. CachedThreadPool
特点:
可缓存的线程池。
线程池中的线程数量不固定,可以根据需要动态增加。
适用于执行大量短期异步任务的场景。
示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* CachedThreadPoolExample
* @author senfel
* @version 1.0
* @date 2024/10/24 11:18
*/
public class CachedThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
3. SingleThreadExecutor
特点:
单线程的线程池。
只有一个工作线程,任务按顺序执行。
适用于需要保证任务顺序执行的场景。
示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* SingleThreadExecutorExample
* @author senfel
* @version 1.0
* @date 2024/10/24 11:18
*/
public class SingleThreadExecutorExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
4. ScheduledThreadPool
特点:
定时任务线程池。
支持定时和周期性任务的执行。
适用于需要定期执行任务的场景。
示例代码:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* ScheduledThreadPoolExample
* @author senfel
* @version 1.0
* @date 2024/10/24 11:19
*/
public class ScheduledThreadPoolExample {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(5);
// 定时任务,延迟1秒后执行
executor.schedule(() -> {
System.out.println("Scheduled task is running on thread " + Thread.currentThread().getName());
}, 1, TimeUnit.SECONDS);
// 周期性任务,初始延迟1秒后开始,每隔2秒执行一次
executor.scheduleAtFixedRate(() -> {
System.out.println("Periodic task is running on thread " + Thread.currentThread().getName());
}, 1, 2, TimeUnit.SECONDS);
// 避免程序立即退出
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executor.shutdown();
}
}
常见错误案例
1. 忘记关闭线程池
错误代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* ThreadPoolShutdownExample
* @author senfel
* @version 1.0
* @date 2024/10/24 11:21
*/
public class ThreadPoolShutdownExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 错误:忘记关闭线程池
// executor.shutdown();
}
}
问题说明:
程序中创建了一个固定大小的线程池,但没有调用executor.shutdown()方法来关闭线程池。
这将导致线程池中的线程一直保持活动状态,即使所有任务已经完成,程序也不会终止。
2. 使用无界队列
错误代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* UnboundedQueueExample
* @author senfel
* @version 1.0
* @date 2024/10/24 11:22
*/
public class UnboundedQueueExample {
public static void main(String[] args) {
ExecutorService executor = new ThreadPoolExecutor(
2, 2, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()
);
for (int i = 0; i < 1000; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
问题说明:
使用了LinkedBlockingQueue作为工作队列,这是一个无界队列。
当任务提交速度远大于任务处理速度时,队列会无限增长,最终可能导致内存溢出。
3. 提交的任务抛出未捕获的异常
错误代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* UncaughtExceptionExample
* @author senfel
* @version 1.0
* @date 2024/10/24 11:23
*/
public class UncaughtExceptionExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
if (taskId == 5) {
throw new RuntimeException("Task 5 failed");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}
问题说明:
在任务中抛出了一个未捕获的异常。
如果不捕获和处理这些异常,线程池可能会默默地忽略这些异常,导致后续任务无法正常执行。
总结
Java线程池是一种强大的多线程管理工具,通过合理选择和配置线程池类型,可以显著提高应用程序的性能和稳定性。本文介绍了四种常见的线程池类型及其示例代码,并分析了一些常见的错误案例。为了避免这些问题,建议:
①始终调用shutdown()方法来关闭线程池。
②使用有界队列来限制任务队列的大小。
③捕获和处理任务中的异常,确保线程池的稳定运行。
希望本文能帮助读者更好地理解和使用Java线程池。