目录
1、线程池简介
- 减少线程的频繁创建、销毁。
- 可有效的使用和管理线程,提升资源重复使用率,避免资源不足引起的一些问题。
四种线程池:
- newCachedThreadPool创建一个可缓存线程池
- newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数。
- newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
- newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务
2、 newFixedThreadPool
原理:创建一个固定数量大小的线程池,当线程任务数超过固定大小时,未执行的线程任务进行阻塞队列,等待线程池调度
示例:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class FixedDemo { public static void main(String[] args) { // 创建一个固定数量的线程池 ExecutorService executor = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { final int idx = i; executor.execute(new Runnable() { // 创建线程并添加到线程池中, 并启动运行线程 @Override public void run() { System.out.println("当前线程名称:" + Thread.currentThread().getName() + ", 参数idx = " + idx); } }); } executor.shutdown(); // 关闭线程池 } }
执行效果图:
说明:创建一个固定数量为5的线程池,并创建10个线程任务添加到线程池中,线程池调度器会分配线程来处理任务。
源码:Executors类中newFixedThreadPool()方法。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
缺点:该方式创建线程池时,会使用LinkedBlockingQueue无界队列默认构造方法,该默认构造方法大小为Integer.MAX_VALUE,所以当线程任务超多时,可能会堆积大量的请求,从而导致 OOM。但一般的系统直接使用该方式即可。
解决方式:直接用new ThreadPoolExecutor的方式创建线程池,指定具体的参数信息。
3、newSingleThreadExecutor
原理:创建一个单例线程池, 只存在一个线程运行, 多的线程任务进行阻塞状态。
示例:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SingleDemo { public static void main(String[] args) { // 创建一个单例线程池, 只存在一个线程运行, 多的线程任务进行阻塞状态 ExecutorService executor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int idx = i; executor.execute(new Runnable() { // 创建线程并添加到线程池中, 并启动运行线程 @Override public void run() { System.out.println("当前线程名称:" + Thread.currentThread().getName() + ", 参数idx = " + idx); } }); } executor.shutdown(); // 关闭线程池 } }
执行效果图:
说明:创建一个单例线程池,并创建10个线程任务添加到线程池中,线程任务被一个一个的顺序执行。
源码:Executors类中newSingleThreadExecutor()方法。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
缺点:同newFixedThreadPool()方法一样。
4、newCachedThreadPool
原理:创建一个缓存线程池,当线程数过大,可回收多余线程,如线程数不足,创建新线程。
示例:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CachedDemo { public static void main(String[] args) { // 创建一个缓存线程池, 当线程数过大,可回收多余线程, 如线程数不足, 创建新线程 ExecutorService executor = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int idx = i; executor.execute(new Runnable() { // 创建线程并添加到线程池中, 并启动运行线程 @Override public void run() { System.out.println("当前线程名称:" + Thread.currentThread().getName() + ", 参数idx = " + idx); } }); } executor.shutdown(); // 关闭线程池 } }
执行效果图:
说明:创建一个缓存线程池,并创建10个线程任务添加到线程池中,由于线程运行速度快,会不断创建新线程来执行任务。
源码:Executors类中newCachedThreadPool()方法。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
缺点:该方式创建线程时,线程的核心数量大小为0,线程最大数量为Integer.MAX_VALUE,此方式允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
解决方式:直接用new ThreadPoolExecutor的方式创建线程池,指定具体的参数信息。
5、newScheduledThreadPool
原理:创建一个定时调度线程池,内置一个延迟队列,可按照设定的固定时间周期性的执行任务。
示例:
import java.text.DateFormat; import java.util.Date; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ScheduledDemo { public static void main(String[] args) { // 创建一个定时调度线程池, 内置一个延迟队列, 可按照设定的固定时间周期性的执行任务 ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); executor.scheduleAtFixedRate(new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); System.out.println("当前线程名: " + Thread.currentThread().getName() + ", scheduleAtFixedRate开始执行时间: " + DateFormat.getTimeInstance().format(new Date())); try { Thread.sleep(5000); // 睡眠5秒钟, 方便测试 } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("当前线程名: " + Thread.currentThread().getName() + ", scheduleAtFixedRate执行花费时间: " + (end - start) / 1000 + "s"); System.out.println("当前线程名: " + Thread.currentThread().getName() + ", scheduleAtFixedRate执行完成时间: " + DateFormat.getTimeInstance().format(new Date())); System.out.println("----------------------分割线-----------------------"); } }, 1, 5, TimeUnit.SECONDS); } }
执行效果图:
说明:创建一个定时调度线程池,添加一个定时调度任务,每隔5秒中执行一遍。
ScheduledFuture<?> scheduleWithFixedDelay (Runnable command, // 要执行的任务 long initialDelay, // 延迟第一次执行的时间 long delay, // 一个执行终止与下一个执行的开始之间的延迟 TimeUnit unit); //initialDelay和delay参数的时间单位 // 一个ScheduledFuture代表待完成的任务,其 get()方法将在取消时抛出异常
缺点:同newCachedThreadPool()方法一样。