• 如何停止一个线程池?


    Java 并发工具包中 java.util.concurrent.ExecutorService 接口定义了线程池任务提交、获取线程池状态、线程池停止的方法等。

    JDK 1.8 中,线程池的停止一般使用 shutdown()、shutdownNow()、shutdown() + awaitTermination(long timeout, TimeUnit unit) 方法。

    1、shutdown() 方法源码中解释

         * Initiates an orderly shutdown in which previously submitted
         * tasks are executed, but no new tasks will be accepted.
         * Invocation has no additional effect if already shut down.
    • 有序关闭,已提交任务继续执行
    • 不接受新任务

    2、shutdownNow() 方法源码中解释

         * Attempts to stop all actively executing tasks, halts the
         * processing of waiting tasks, and returns a list of the tasks
         * that were awaiting execution.
    • 尝试停止所有正在执行的任务
    • 停止等待执行的任务,并返回等待执行的任务列表

    3、awaitTermination(long timeout, TimeUnit unit) 方法源码中解释

         * Blocks until all tasks have completed execution after a shutdown
         * request, or the timeout occurs, or the current thread is
         * interrupted, whichever happens first.
         *
         * @param timeout the maximum time to wait
         * @param unit the time unit of the timeout argument
         * @return {@code true} if this executor terminated and
         *         {@code false} if the timeout elapsed before termination
         * @throws InterruptedException if interrupted while waiting
    • 收到关闭请求后,所有任务执行完成、超时、线程被打断,阻塞直到三种情况任意一种发生
    • 参数可以设置超时时间与超时单位
    • 线程池关闭返回 true;超过设置时间未关闭,返回 false

    实践:

    1、使用 Executors.newFixedThreadPool(int nThreads) 创建固定大小线程池,测试 shutdown() 方法

    package constxiong.concurrency.a013;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 测试固定数量线程池 shutdown() 方法
     * @author ConstXiong
     */
    public class TestFixedThreadPoolShutdown {
    	
    	public static void main(String[] args) {
    		//创建固定 3 个线程的线程池
    		ExecutorService threadPool = Executors.newFixedThreadPool(3);
    		
    		//向线程池提交 10 个任务
    		for (int i = 1; i <= 10; i++) {
    			final int index = i;
    			threadPool.submit(() -> {
    				System.out.println("正在执行任务 " + index);
    				//休眠 3 秒
    				try {
    					Thread.sleep(3000);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			});
    		}
    		
    		//休眠 4 秒
    		try {
    			Thread.sleep(4000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    
    		//关闭线程池
    		threadPool.shutdown();
    	}
    
    }
    

    打印结果如下,可以看出,主线程向线程池提交了 10 个任务,休眠 4 秒后关闭线程池,线程池把 10 个任务都执行完成后关闭了。

    正在执行任务 1
    正在执行任务 3
    正在执行任务 2
    正在执行任务 4
    正在执行任务 6
    正在执行任务 5
    正在执行任务 8
    正在执行任务 9
    正在执行任务 7
    正在执行任务 10

    2、使用 Executors.newFixedThreadPool(int nThreads) 创建固定大小线程池,测试 shutdownNow() 方法

    package constxiong.concurrency.a013;
    
    import java.util.List;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * 测试固定数量线程池 shutdownNow() 方法
     * @author ConstXiong
     */
    public class TestFixedThreadPoolShutdownNow {
    	
    	public static void main(String[] args) {
    		//创建固定 3 个线程的线程池
    		ExecutorService threadPool = Executors.newFixedThreadPool(3);
    		
    		//向线程池提交 10 个任务
    		for (int i = 1; i <= 10; i++) {
    			final int index = i;
    			threadPool.submit(() -> {
    				System.out.println("正在执行任务 " + index);
    				//休眠 3 秒
    				try {
    					Thread.sleep(3000);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			});
    		}
    		
    		//休眠 4 秒
    		try {
    			Thread.sleep(4000);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    
    		//关闭线程池
    		List<Runnable> tasks = threadPool.shutdownNow();
    		System.out.println("剩余 " + tasks.size() + " 个任务未执行");
    	}
    
    }
    

    打印结果如下,可以看出,主线程向线程池提交了 10 个任务,休眠 4 秒后关闭线程池,线程池执行了 6 个任务,抛出异常,打印返回的剩余未执行的任务个数。

    正在执行任务 1
    正在执行任务 2
    正在执行任务 3
    正在执行任务 4
    正在执行任务 6
    正在执行任务 5
    剩余 4 个任务未执行
    java.lang.InterruptedException: sleep interrupted
    	at java.lang.Thread.sleep(Native Method)
    	at constxiong.concurrency.a013.TestFixedThreadPoolShutdownNow.lambda$0(TestFixedThreadPoolShutdownNow.java:24)
    	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    	at java.lang.Thread.run(Thread.java:748)
    java.lang.InterruptedException: sleep interrupted
    	at java.lang.Thread.sleep(Native Method)
    	at constxiong.concurrency.a013.TestFixedThreadPoolShutdownNow.lambda$0(TestFixedThreadPoolShutdownNow.java:24)
    	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    	at java.lang.Thread.run(Thread.java:748)
    java.lang.InterruptedException: sleep interrupted
    	at java.lang.Thread.sleep(Native Method)
    	at constxiong.concurrency.a013.TestFixedThreadPoolShutdownNow.lambda$0(TestFixedThreadPoolShutdownNow.java:24)
    	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    	at java.lang.Thread.run(Thread.java:748)
    

    3、Executors.newFixedThreadPool(int nThreads) 创建固定大小线程池,测试 awaitTermination(long timeout, TimeUnit unit) 方法

    package constxiong.concurrency.a013;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    
    /**
     * 测试固定数量线程池 shutdownNow() 方法
     * @author ConstXiong
     */
    public class TestFixedThreadPoolAwaitTermination {
    	
    	public static void main(String[] args) {
    		//创建固定 3 个线程的线程池
    		ExecutorService threadPool = Executors.newFixedThreadPool(3);
    		
    		//向线程池提交 10 个任务
    		for (int i = 1; i <= 10; i++) {
    			final int index = i;
    			threadPool.submit(() -> {
    				System.out.println("正在执行任务 " + index);
    				//休眠 3 秒
    				try {
    					Thread.sleep(3000);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			});
    		}
    		
    		//关闭线程池,设置等待超时时间 3 秒
    		System.out.println("设置线程池关闭,等待 3 秒...");
    		threadPool.shutdown();
    		try {
    			boolean isTermination = threadPool.awaitTermination(3, TimeUnit.SECONDS);
    			System.out.println(isTermination ? "线程池已停止" : "线程池未停止");
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		
    		//再等待超时时间 20 秒
    		System.out.println("再等待 20 秒...");
    		try {
    			boolean isTermination = threadPool.awaitTermination(20, TimeUnit.SECONDS);
    			System.out.println(isTermination ? "线程池已停止" : "线程池未停止");
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    }
    

    打印结果如下,可以看出,主线程向线程池提交了 10 个任务,申请关闭线程池 3 秒超时,3 秒后线程池并未成功关闭;再获取线程池关闭状态 20 秒超时,线程池成功关闭。

    正在执行任务 1
    正在执行任务 3
    正在执行任务 2
    设置线程池关闭,等待 3 秒...
    线程池未停止
    正在执行任务 4
    正在执行任务 6
    再等待 20 秒...
    正在执行任务 5
    正在执行任务 7
    正在执行任务 9
    正在执行任务 8
    正在执行任务 10
    线程池已停止
    

    总结:

    1. 调用 shutdown() 和 shutdownNow() 方法关闭线程池,线程池都无法接收新的任务
    2. shutdown() 方法会继续执行正在执行未完成的任务;shutdownNow() 方法会尝试停止所有正在执行的任务
    3. shutdown() 方法没有返回值;shutdownNow() 方法返回等待执行的任务列表
    4. awaitTermination(long timeout, TimeUnit unit) 方法可以获取线程池是否已经关闭,需要配合 shutdown() 使用
    5. shutdownNow() 不一定能够立马结束线程池,该方法会尝试停止所有正在执行的任务,通过调用 Thread.interrupt() 方法来实现的,如果线程中没有 sleep() 、wait()、Condition、定时锁等应用, interrupt() 方法是无法中断当前的线程的。


     


     

    所有资源资源汇总于公众号



     

  • 相关阅读:
    线程间的通信 与 线程池
    线程同步
    静态代理模式
    多线程状态
    线程、进程、多线程
    Java面向对象之泛型
    ConstraintLayout 用法
    搞NDK开发
    Linux基础命令【记录】
    c# 的一些基本操作或属性
  • 原文地址:https://www.cnblogs.com/ConstXiong/p/11955747.html
Copyright © 2020-2023  润新知