线程池?看这篇就够了

涉及到线程池之前,我们需要知道创建线程的几种方式,在线程的基础之上,我们进一步学习线程池。

博主魔改博客链接位置:欢迎各位大大们来观看。
博主魔改博客本文地址

线程池

线程池是什么?

线程池是一种池化技术,使用池化技术管理和使用线程的一种机制。

池化技术:在利用资源之前,我们需要提前准备一些资源,在需要时候重复使用提前准备的资源。

池化技术(Pooling)是一种常用的优化技术,它的目的是通过重复使用已经创建的对象或者资源,来避免频繁的创建和销毁对象或者资源所带来的开销。通过池化技术,可以减少创建和销毁对象的开销,从而提高程序的性能和稳定性。在计算机领域中,池化技术通常用于以下几个方面:

  1. 数据库连接池:通过重复使用已经创建的数据库连接,避免频繁地创建和销毁数据库连接所带来的开销。
  2. 线程池:通过重复使用已经创建的线程,避免频繁地创建和销毁线程所带来的开销。
  3. 对象池:通过重复使用已经创建的对象,避免频繁地创建和销毁对象所带来的开销。
  4. 内存池:通过重复使用已经分配的内存,避免频繁地分配和释放内存所带来的开销。

在实际应用中,池化技术可以帮助我们提高程序的性能和稳定性,因此在设计和开发程序时,可以考虑采用池化技术来优化程序的性能。

线程池的使用方式

  • 通过ThreadPoolExecutor创建的线程池;
  • 通过Executors创建的线程池。

线程池的创建方式总共包含以下7种(其中6种是通过Executors 创建的,1种是通过ThreadPoolExecutor创建的):

  • Executors.newFixedThreadPool :创建一个固定大小的线程池,可以控制并发的线程数,超出的线程会在队列中等待;
  • Executors.newCachedThreadPool :创建一个可缓存的线程池,若线程超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;
  • Executors.newScheduledThreadPool :创建一个可以执行延迟任务的线程池;
  • Executors.newSingleThreadExecutor :创建单个线程的线程池,它可以保证先进先出的执行顺序
  • Executors.newSingleThreadScheduledExecutor :创建一个可以执行延迟任务的单个线程的线程池;
  • Executors.newWorkStealingPool :创建一个抢占式执行的线程池;
  • ThreadPoolExecutor :手动创建线程池 的方式,它最多包含了7个参数可供设置,最少可设置5个参数。

Executors

创建固定数量的线程池

java 复制代码
  public class Demo1 {
    	public static void main(String[] args) {
    		//创建一个包含5个线程的线程池
    		ExecutorService threadPool = Executors.newFixedThreadPool(5);
    		//给予10个资源分配给5个线程去分
    		for(int i = 0 ; i < 10 ; i ++) {
    			threadPool.execute(new Runnable() {
    				@Override
    				public void run() {
    					System.out.println("线程名称:" + Thread.currentThread().getName());
    				}
    			});
    		}
    		//执行完毕后记得关闭。预防占用资源
    		threadPool.shutdown();
    	}
    }
java 复制代码
 public class Demo2 {
    	public static void main(String[] args) throws InterruptedException, ExecutionException {
    		//创建5个线程
    		ExecutorService threadPool = Executors.newFixedThreadPool(5);
    		//使用线程池执行任务,也给线程池添加任务
    		for(int i = 0; i < 5 ; i ++) {
    			//使用Callable据接口来创建线程
    			Future<Integer> result = threadPool.submit(new Callable<Integer>() {
    				@Override
    				public Integer call() throws Exception {
    					int num = new Random().nextInt(9);
    					System.out.println("随机数:" + num);
    					return num;
    				}
    			});
    			System.out.println("线程池的返回结果:" + result.get());
    		}
            		//执行完毕后记得关闭。预防占用资源
    		threadPool.shutdown();
    	}
    }

通过上述两种方式,我发现向线程池中添加任务的方式有两种:

  • execute : 只执行不带返回值的任务
  • submit:可以执行又返回值的任务或者没有返回值的任务。

线程池工厂

在上面的执行过程中,可以看到没有设置线程名称时候,他就会是一个默认名,线程工厂可以让我们自定义线程名称或者优先级。

java 复制代码
public class Demo3 {
    public static void main(String[] args) {
       //1.创建线程工厂
        ThreadFactory threadFactory=new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread=new Thread(r);
                //设置线程命名规则
                thread.setName("我的线程-"+r.hashCode());
                //设置线程优先级
                thread.setPriority(Thread.MAX_PRIORITY);
                return thread;
            }
        };
        ExecutorService threadPool = Executors.newFixedThreadPool(5, threadFactory);
        for(int i=0;i<5;i++){
            threadPool.submit(()->{
                //任务
                Thread thread = Thread.currentThread();
                System.out.println("线程池开始执行了:"+thread.getName()+" "+thread.getPriority());
            });
        }
        //当程序执行完毕后记得关闭线程池
        threadPool.shutdown();
        
    }
}

线程池开始执行了:我的线程-1826771953 10

线程池开始执行了:我的线程-455659002 10

线程池开始执行了:我的线程-245257410 10

线程池开始执行了:我的线程-1705736037 10

线程池开始执行了:我的线程-1406718218 10

带缓存的线程池

java 复制代码
 public class Demo4 {
    	public static void main(String[] args) {
            ExecutorService threadPool = Executors.newCachedThreadPool();
            for(int i=0; i<1000; i++){
                int finalI=i;
                threadPool.submit(()->{
                    System.out.println("i:"+finalI+"|线程名称:"+Thread.currentThread().getName());
                });
            }
            //当程序执行完毕后记得关闭线程池
            threadPool.shutdown();
        }
    }

在此我们发现,我们给线程一千个任务的时候,他并不会创建1000个线程,我们通过执行发现最多会创建100多个线程,然后会进行复用。

故此我们在这里总结一下:

线程池中的线程也不能无限制地被创建。线程池中的线程数量应该根据实际情况进行适当的设置,以便充分利用系统资源,提高系统的性能和稳定性。如果线程池中的线程数量过多,会导致系统资源的耗尽,从而导致系统的性能下降或崩溃。另外,线程的创建和销毁也会带来一定的开销,如果线程池中的线程数量过多,会增加线程的创建和销毁开销,从而降低系统的性能和稳定性。

因此,在实际应用中,需要根据实际情况来设置线程池的大小,以满足应用的需要,并保证系统的性能和稳定性。一般来说,线程池中的线程数量应该根据系统的 CPU 核心数、内存大小、任务类型和系统负载等因素来进行设置,以达到最优的效果。

执行定时任务的线程池

延迟执行一次:

java 复制代码
public class Demo5 {
	
	public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
        System.out.println("添加任务的时间:"+ LocalDate.now());
        scheduleTest(service);//只执行一次的定时任务
	}
    /**
     * 只执行一次的定时任务
     * @param service
     */
    private static void scheduleTest(ScheduledExecutorService service) {
        //执行定时任务(延迟3秒执行)
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行了任务:"+LocalDateTime.now());
            }
        },3, TimeUnit.SECONDS);
        //当循环执行完毕后记得关闭线程池
        service.shutdown();
    }

}

本延迟执行任务,只能执行一次。

创建这个线程池有3个参数

  • 执行任务
  • 延迟n秒执行
  • 配合执行的时间单位

固定频率执行

我们可以让线程以以固定频率间隔n秒执行,创建这个线程池有四个参数:

  1. 执行任务
  2. 延迟n秒执行
  3. 执行定时任务的频率
  4. 配合3执行的时间单位
java 复制代码
 public class Demo6 {
    	public static void main(String[] args) {
            ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
            System.out.println("添加任务的时间:"+ LocalDateTime.now());
    
            //2S之后开始执行定时任务,定时任务每4s执行一次
            service.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    System.out.println("执行任务:"+LocalDateTime.now());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },2,4,TimeUnit.SECONDS);
            //由于是定时任务,故而不能够让他关闭,我们需要通过循环来控制关闭,这里我们就不在描述
            // service.shutdown();
        }
    }
java 复制代码
public class Demo7 {
	public static void main(String [] args) {
	ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
    System.out.println("添加任务的时间:"+ LocalDateTime.now());
//    //2S之后开始执行定时任务,定时任务每4s执行一次
//    service.scheduleAtFixedRate(new Runnable() {
//        @Override
//        public void run() {
//            System.out.println("执行任务:"+LocalDateTime.now());
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        }
//    },2,4,TimeUnit.SECONDS);
    //2s之后开始执行,每次执行间隔4s
    service.scheduleWithFixedDelay(new Runnable() {
        @Override
        public void run() {
            System.out.println("执行任务:"+LocalDateTime.now());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    },2,4,TimeUnit.SECONDS);
  }
}

在 Java 中,Timer 类提供了两个方法用于定时执行任务,分别是 scheduleAtFixedRate () 和 scheduleWithFixedDelay()。这两个方法都可以实现定时执行任务的功能,但它们的执行方式略有不同,具体区别如下:

  1. scheduleAtFixedRate() 方法:该方法是按照固定的时间间隔执行任务,它会按照指定的时间间隔不断地执行任务,无论上一次任务是否执行完成,也不考虑任务的执行时间,即使任务的执行时间超过了时间间隔,也会按照指定的时间间隔再次执行任务。
  2. scheduleWithFixedDelay() 方法:该方法是在任务执行完成后,等待指定的时间间隔再次执行任务,它会在任务执行完成后,等待指定的时间间隔,然后再次执行任务,考虑了任务的执行时间。也就是说,该方法会在上一次任务执行完成后,等待指定的时间间隔,然后再次执行任务。

因此,如果需要按照固定的时间间隔执行任务,无论任务是否执行完成,都需要按照指定的时间间隔再次执行任务,可以使用 scheduleAtFixedRate() 方法;如果需要在任务执行完成后,等待指定的时间间隔再次执行任务,可以使用 scheduleWithFixedDelay() 方法。

单线程的线程池

java 复制代码
 public class Demo8 {
    	public static void main(String[] args) {
    		ExecutorService executorService = Executors.newSingleThreadExecutor();
    		for (int i = 0; i < 10; i++) {
    			int finalI=i;
    			executorService.submit(new Runnable() {
    				@Override
    				public void run() {
    					System.out.println("任务名:"+finalI+"线程名:"+Thread.currentThread().getName());
    				}
    			});
    		}
    		//老样子执行完毕后记得关闭
    		executorService.shutdown();
    	}
    }

单线程的线程池和单线程有什么区别?以及其各自的作用?

单线程的线程池和单线程之间有以下几点区别:

  1. 线程池是一种可以管理和调度多个线程的机制,可以重复利用线程,从而避免频繁创建和销毁线程的开销,提高程序的性能和稳定性。而单线程则只能执行一个任务,无法进行任务的并行处理。
  2. 线程池中的线程数量可以根据实际需求进行动态调整,可以增加或减少线程的数量,以适应不同的负载和任务类型。而单线程则只能处理一个任务,无法进行线程数量的动态调整。
  3. 线程池可以管理和调度多个任务。

定时任务的单线程线程池

java 复制代码
  public class Demo9 {
    	public static void main(String[] args) {
    		ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
    		
    		System.out.println("执行任务的时间:" + LocalDateTime.now());
    		service.schedule(new Runnable() {
    			@Override
    			public void run() {
    				System.out.println("执行任务:" + LocalDateTime.now());
    			}
    			// 执行任务 2秒后执行 ,单位 TimeUnit.Seconds秒
    		}, 2, TimeUnit.SECONDS);
    		//当定时任务执行完毕后记得关闭
    		service.shutdown();
    	}
    }

根据当前CPU生成线程池

java 复制代码
 public class Demo10 {
    	
    	public static void main(String[] args) {
    		//根据当前电脑的CPU来创建线程池
    		ExecutorService service = Executors.newWorkStealingPool();
    		for(int i = 0 ; i < 100; i ++ ) {
    			int finaIi  = i ;
    			service.submit(()->{
    				System.out.println("任务:" + finaIi + "线程名:" + Thread.currentThread());
    			});
    		}
    		//判断线程是否
    		while(!service.isTerminated()) {
    			//执行完毕后记得关闭线程池
    			service.shutdown();
    		};
    	}
    
    }

ThreadPoolExecutor

ThreadPoolExecutor和Executors比较

都是Java中用于生成线程池的类,它们有以下几个区别:

  1. ThreadPoolExecutor是一个更底层的类 ,它提供了更多的配置选项,如线程池的核心线程数、最大线程数、任务队列等,可以更加灵活地控制线程池的行为。而Executors则是在ThreadPoolExecutor基础上进行了封装,提供了一些预定义的线程池配置选项,如newFixedThreadPool、newCachedThreadPool等,方便用户快速创建线程池。
  2. ThreadPoolExecutor可以通过自定义RejectedExecutionHandler来处理任务被拒绝执行的情况,而Executors只提供了一些简单的拒绝策略,如抛出异常、丢弃任务等。
  3. Executors生成的线程池通常是无界队列 的,如果任务数量过多,可能会导致内存溢出 等问题。而ThreadPoolExecutor则可以通过配置有界队列或者拒绝策略来避免这种情况。
  4. 在Java 8之前,Executors生成的线程池中的工作线程都是非守护线程 ,即使主线程结束,工作线程也会继续执行。而ThreadPoolExecutor则可以通过设置工作线程为守护线程来避免这种情况。

ThreadPoolExecutor和Executors各自可以解决的问题也有所差异。ThreadPoolExecutor适合于需要更精细的线程池配置、需要自定义拒绝策略、需要有界队列等情况。而Executors则适合于快速创建线程池,且任务数量不会过多的情况。需要根据具体场景选择合适的类来生成线程池。

Executors存在的问题

虽然Executors可以快速创建线程池,但在一些情况下可能会存在问题,如下:

  1. FixedThreadPool和SingleThreadExecutor的线程数是固定的,如果任务数量过多,会导致队列中的任务堆积,从而占用大量内存,甚至导致OOM异常。
  2. CachedThreadPool的最大线程数是Integer.MAX_VALUE,如果任务数量过多,会创建大量线程,占用大量系统资源,导致系统崩溃。
  3. Executors生成的线程池中的工作线程都是非守护线程,即使主线程结束,工作线程也会继续执行。如果应用程序没有显式地终止线程池,会导致应用程序无法正常退出,从而导致内存泄漏等问题。
  4. Executors提供的拒绝策略有一些局限性,例如在任务被拒绝后,无法重新提交任务等。

因此,对于不同的应用场景,应该选择合适的线程池类型或者直接使用ThreadPoolExecutor来手动创建线程池,以避免出现上述问题。

ThreadPoolExecutor的介绍

ThreadPoolExecutor是Java中用于创建线程池的类,它有以下几个参数:

  1. corePoolSize :线程池的核心线程数,即线程池中始终存在的线程数。如果线程池中的线程数小于corePoolSize,则会创建新的线程来执行任务,直到线程数等于corePoolSize。
  2. maximumPoolSize :线程池的最大线程数,即线程池中最多能存在的线程数。如果任务数量超过了线程池的容量,则会根据拒绝策略处理这些任务。
  3. keepAliveTime线程池中非核心线程的空闲存活时间。如果线程池中的线程数大于corePoolSize,并且某个线程空闲的时间超过了keepAliveTime,则该线程会被销毁直到线程池中的线程数等于corePoolSize。
  4. TimeUnitkeepAliveTime的时间单位,例如TimeUnit.SECONDS。
  5. workQueue线程池中的任务队列,用于存储等待执行的任务。可以选择不同类型的队列,如有界队列和无界队列。
  6. threadFactory线程工厂,用于创建新的线程。可以自定义线程的名称、优先级等属性。
  7. handler拒绝策略,用于处理无法执行的任务。可以选择不同的策略,如抛出异常、丢弃任务、等待一段时间再重试等。

这些参数的具体作用如下:

  1. 通过corePoolSize和maximumPoolSize来控制线程池中的工作线程数量,以达到最优的性能和资源利用效率。
  2. 通过keepAliveTime和workQueue来控制线程池中线程的存活时间和任务的排队策略,以避免任务堆积和资源浪费。
  3. 通过threadFactory可以自定义线程的属性,如线程名称等。

当我们使用ThreaPoolExecutor创建线程池的时候,默认最少使用5个参数

java 复制代码
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue)
java 复制代码
 public class Demo1 {
    	public static void main(String[] args) {
    		ThreadFactory factory = new ThreadFactory() {
    
    			@Override
    			public Thread newThread(Runnable r) {
    				Thread thread = new Thread(r);
    				return thread;
    			}
    		};
    		ThreadPoolExecutor executor = new ThreadPoolExecutor(
    				2,
    				5,
    				10,
    				TimeUnit.SECONDS,
    				new LinkedBlockingDeque<Runnable>(2),
    				factory,
    				new ThreadPoolExecutor.DiscardPolicy());
    		for(int i = 1 ; i < 6 ; i ++) {
    			int finaIi = i ;
    			executor.submit(()->{
    				try {
    					Thread.sleep(10 * finaIi);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    				System.out.println("任务:" + finaIi);
    			});
    		}
            //执行循环完毕后关闭线程池
    		executor.shutdown();
    	}
    }

线程池的执行流程

线程池的执行流程如下:

  1. 首先,线程池会创建一些初始的核心线程,并将它们放入工作队列中。
  2. 当有任务进来时,线程池会从工作队列中选取一个线程来执行任务
  3. 如果此时线程池中的线程数小于核心线程数(corePoolSize)且有空闲线程,则会立即创建新的线程来执行任务
  4. 如果此时线程池中的线程数已经达到了核心线程数,且工作队列已满,则会将任务提交到线程池的任务队列中
  5. 如果此时任务队列已满,且线程池中的线程数小于最大线程数(maximumPoolSize),则会创建新的线程来执行任务。
  6. 如果此时线程池中的线程数已经达到了最大线程数,且任务队列已满,则会按照拒绝策略(RejectedExecutionHandler)来处理任务。常见的拒绝策略有:抛出异常、丢弃任务、等待一段时间再重试等。
  7. 某个线程执行完任务后,会从任务队列中取出下一个任务继续执行,直到线程池关闭或者出现异常

需要注意的是,线程池的执行流程是异步的,任务的执行顺序是不确定的。线程池在执行任务时,会根据任务的优先级、执行时间等因素来选择执行顺序,具体的执行流程会根据不同的线程池实现而有所差异。

拒绝策略

目前线程池的拒绝策略共计有5种,其中有四种JDK提供的和一种自定义拒绝策略。

线程池的拒绝策略指的是当线程池中的线程已经全部被占用,队列也已满,无法继续接受新的任务时,应该如何处理这些被拒绝的任务。常见的线程池拒绝策略包括:

  1. AbortPolicy(默认策略):直接抛出RejectedExecutionException异常,表示拒绝执行该任务。
  2. CallerRunsPolicy:由调用线程处理该任务,即在提交任务的线程中直接执行该任务。这种策略可能会降低整个系统的吞吐量,因为提交任务的线程可能不是专门用来处理任务的线程,而是业务线程。
  3. DiscardPolicy:直接丢弃该任务,不做任何处理。
  4. DiscardOldestPolicy:抛弃最早加入队列的任务,并尝试再次提交当前任务。
  5. 自定义拒绝策略:用户可以根据业务场景,自定义拒绝策略,例如将任务记录到日志中、将任务放到消息队列中等等。

选择合适的线程池拒绝策略,可以更好地保障系统的稳定性和可靠性。

第一种AbortPolicy
java 复制代码
public class Demo2 {
	public static void main(String[] args) {
		ThreadFactory factory = new ThreadFactory() {
			@Override
			public Thread newThread(Runnable r) {
				Thread thread = new Thread(r);
				return thread;
			}
		};
		
		ThreadPoolExecutor executor = new ThreadPoolExecutor(
				2,
				2,
				10,
				TimeUnit.SECONDS,
				new LinkedBlockingDeque<>(2),
				factory,
				new ThreadPoolExecutor.AbortPolicy() //拒绝策略
				);
		for(int i = 0 ; i < 5; i ++) {
			int finaIi = i;
			executor.submit(()->{
				try {
					Thread.sleep(10*finaIi);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("任务:" + finaIi);
			});
		}
        //执行循环完毕后关闭线程池
				executor.shutdown();
	}
}
第二种CallerRunsPolicy()
java 复制代码
 import java.util.concurrent.*;
    
    /**
     * 手动方式创建线程池
     */
    public class Demo3 {
        public static void main(String[] args) {
            ThreadFactory factory=new ThreadFactory() {
                @Override
                public Thread newThread(Runnable r) {
                    Thread thread=new Thread(r);
                    return thread;
                }
            };
            ThreadPoolExecutor executor=new ThreadPoolExecutor(
                2,
                2,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(2),
                factory,
                new ThreadPoolExecutor.CallerRunsPolicy());  //拒绝策略
            for (int i = 0; i < 5; i++) {
                int finalI=i;
                executor.submit(()->{
                    try {
                        Thread.sleep(10*finalI);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("任务:"+finalI);
                });
            }
            //执行循环完毕后关闭线程池
    				executor.shutdown();
        }
    }
第三种:DiscardPolicy ()
java 复制代码
public class Demo4 {
    public static void main(String[] args) {
        ThreadFactory factory=new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread=new Thread(r);
                return thread;
            }
        };
        ThreadPoolExecutor executor=new ThreadPoolExecutor(
        		2,
        		2,
        		10,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(2),
                factory,
                new ThreadPoolExecutor.DiscardPolicy()
                );
        for (int i = 0; i < 5; i++) {
            int finalI=i;
            executor.submit(()->{
                try {
                    Thread.sleep(10*finalI);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("任务:"+finalI);
            });
        }
        //执行循环完毕后关闭线程池
				executor.shutdown();
    }
}
第四种:DiscardOldestPolicy()
java 复制代码
public class Demo5 {
    public static void main(String[] args) {
        ThreadFactory factory=new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread=new Thread(r);
                return thread;
            }
        };
        ThreadPoolExecutor executor=new ThreadPoolExecutor(
        		2,
        		2,
        		10,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(2),
                factory,
                new ThreadPoolExecutor.DiscardOldestPolicy() //拒绝策略
                );
        for (int i = 0; i < 5; i++) {
            int finalI=i;
            executor.submit(()->{
                try {
                    Thread.sleep(10*finalI);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("任务:"+finalI);
            });
        }
        //执行循环完毕后关闭线程池
				executor.shutdown();
    }
}
第五种:自定义拒绝策略
java 复制代码
public class Demo6 {
    public static void main(String[] args) {
        ThreadFactory factory=new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread=new Thread(r);
                return thread;
            }
        };
        ThreadPoolExecutor executor=new ThreadPoolExecutor(
        		2,
        		2,
        		10,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(2),
                factory,
                new RejectedExecutionHandler() {  //自定义拒绝策略
					
					@Override
					public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
						System.out.println("我执行了自定义拒绝策略");
					}
        		});
        for (int i = 0; i < 5; i++) {
            int finalI=i;
            executor.submit(()->{
                try {
                    Thread.sleep(10*finalI);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("任务:"+finalI);
            });
        }
        //执行循环完毕后关闭线程池
				executor.shutdown();
    }
}

线程池的状态

线程池的状态通常包括以下几种:

  1. RUNNING :线程池处于运行状态,可以接受新任务并处理已有的任务。
  2. SHUTDOWN :线程池处于关闭状态,不再接受新任务,但会处理已有的任务。
  3. STOP :线程池处于停止状态,不再接受新任务,也不会处理已有的任务,会中断正在执行的任务。
  4. TIDYING :线程池正在整理线程池中的线程,例如删除已经停止的线程。
  5. TERMINATED :线程池已经终止,不再接受任何任务。

线程池的状态可以通过ThreadPoolExecutor类的getState()方法获取,返回一个枚举类型的值,表示当前线程池的状态。在使用线程池的过程中,需要根据当前线程池的状态,避免不必要的操作,保证线程池的稳定性和可靠性

  1. RUNNING状态的例子:
java 复制代码
   ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
    executor.execute(new Runnable() {
        @Override
        public void run() {
            System.out.println("Task executed.");
        }
    });
    System.out.println("Current thread pool state: " + executor.getState());
    // 输出:Current thread pool state: RUNNING

这个例子中,线程池处于RUNNING状态,因为线程池已经创建成功,可以接受新任务并处理已有的任务。

  1. SHUTDOWN状态的例子:
java 复制代码
 ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
    executor.shutdown();
    System.out.println("Current thread pool state: " + executor.getState());
    // 输出:Current thread pool state: SHUTDOWN

这个例子中,线程池处于SHUTDOWN状态,因为调用了executor.shutdown()方法,线程池不再接受新任务,但会处理已有的任务。

  1. STOP状态的例子:
java 复制代码
 ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
    executor.execute(new Runnable() {
        @Override
        public void run() {
            while (true) {
                System.out.println("Task executed.");
            }
        }
    });
    executor.shutdown();
    try {
        executor.awaitTermination(1, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("Current thread pool state: " + executor.getState());
    // 输出:Current thread pool state: STOP

这个例子中,线程池处于STOP状态,因为调用了executor.shutdown()方法后,线程池不再接受新任务,并且中断正在执行的任务。

  1. TIDYING状态的例子:
java 复制代码
 ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
    executor.shutdown();
    while (!executor.isTerminated()) {
        System.out.println("Current thread pool state: " + executor.getState());
    }
    // 输出:
    // Current thread pool state: TIDYING
    // Current thread pool state: TERMINATED

这个例子中,线程池处于TIDYING状态,因为调用了executor.shutdown()方法后,线程池正在整理线程池中的线程,例如删除已经停止的线程。

  1. TERMINATED状态的例子:
java 复制代码
 ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
    executor.shutdown();
    try {
        executor.awaitTermination(1, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("Current thread pool state: " + executor.getState());
    // 输出:Current thread pool state: TERMINATED

这个例子中,线程池处于TERMINATED状态,因为线程池已经终止,不再接受任何任务。在调用executor.shutdown()方法后,等待所有任务执行完毕并且所有线程都被回收后,线程池进入TERMINATED状态。

关闭线程池的方法

ThreadPoolExecutor类提供了两种关闭线程池的方法,分别是shutdown()和shutdownNow()。这两种方法的区别如下:

  1. shutdown()方法:调用该方法后,线程池会拒绝新的任务提交,但会继续处理已经提交的任务,直到所有任务都被完成 。在所有任务完成后,线程池才会真正关闭,并释放所有资源。如果在调用shutdown()方法之后,继续提交新的任务,这些任务会被拒绝并抛出RejectedExecutionException异常。
  2. shutdownNow()方法:调用该方法后,线程池会拒绝新的任务提交,并尝试中断正在执行的任务。在中断任务的过程中,如果任务响应中断,则任务会被成功中断并从任务队列中移除。如果任务无法响应中断,则任务会继续执行。在所有任务都被中断或已经完成后,线程池会真正关闭,并释放所有资源。如果在调用shutdownNow()方法之后,继续提交新的任务,这些任务会被拒绝并抛出RejectedExecutionException异常。

因此,shutdown()方法是优雅地关闭线程池,等待所有任务都被完成后再关闭线程池,而shutdownNow()方法是强制关闭线程池,立即停止所有任务的执行,并尝试中断正在执行的任务。选择哪个方法取决于业务需求,如果需要优雅地关闭线程池,可以使用shutdown()方法,如果需要立即停止所有任务的执行,可以使用shutdownNow()方法。

相关推荐
ID_18007905473几秒前
Python 实现亚马逊商品详情 API 数据准确性校验(极简可用 + JSON 参考)
java·python·json
c++之路20 分钟前
C++23概述
java·c++·c++23
专注API从业者1 小时前
Open Claw 京东商品监控选品实战:一键抓取、实时监控、高效选品
java·服务器·数据库
摇滚侠2 小时前
DBeaver 导入数据库 导入 SQL 文件 MySQL 备份恢复
java·数据库·mysql
keep one's resolveY2 小时前
SpringBoot实现重试机制的四种方案
java·spring boot·后端
天空属于哈夫克32 小时前
企业微信API常见的错误和解决方案
java·数据库·企业微信
摇滚侠3 小时前
VMvare 虚拟机 Oracle19c 安装步骤,远程连接 Oracle19c,百度网盘安装包
java·oracle
梁萌3 小时前
idea报错找不到XX包的解决方法
java·intellij-idea·启动报错·缺少包
Agent产品评测局3 小时前
生产排期与MES/ERP系统打通,实操方法详解 —— 2026企业级智能体自动化选型与实战指南
java·运维·人工智能·ai·chatgpt·自动化
阿丰资源4 小时前
基于Spring Boot的电影城管理系统(直接运行)
java·spring boot·后端