线程池?看这篇就够了

如果你不相信努力和时光,那么成果就会是第一个选择辜负你的。不要去否定你自己的过去,也不要用你的过去牵扯你现在的努力和对未来的展望。不是因为拥有希望你才去努力,而是去努力了,你才有可能看到希望的光芒。线程池?看这篇就够了,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

涉及到线程池之前,我们需要知道创建线程的几种方式,在线程的基础之上,我们进一步学习线程池。
博主魔改博客链接位置:欢迎各位大大们来观看。
博主魔改博客本文地址

线程池

线程池是什么?

线程池是一种池化技术,使用池化技术管理和使用线程的一种机制。
池化技术:在利用资源之前,我们需要提前准备一些资源,在需要时候重复使用提前准备的资源。
池化技术(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

创建固定数量的线程池

  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();
    	}
    }
 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:可以执行又返回值的任务或者没有返回值的任务。

线程池工厂

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

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

带缓存的线程池

 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 核心数、内存大小、任务类型和系统负载等因素来进行设置,以达到最优的效果。

执行定时任务的线程池

延迟执行一次:

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执行的时间单位
 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();
        }
    }
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() 方法。

单线程的线程池

 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. 线程池可以管理和调度多个任务。

定时任务的单线程线程池

  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生成线程池

 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个参数

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue)
 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
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()
 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 ()
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()
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();
    }
}
第五种:自定义拒绝策略
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状态的例子:
   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状态的例子:
 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状态的例子:
 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状态的例子:
 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状态的例子:
 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()方法。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/197971.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!