详解Java如何关闭线程以及线程池

目录
  • 前言
  • 1. 关闭线程
    • 1.1 volatile关键字
    • 1.2 intrrrupt()方法
  • 2.关闭线程池
    • 2.1 shutdownNow()方法
    • 2.2 shutdown()方法

前言

这个问题是一个高频的面试题

而且在印象中是由stop方法执行或者终端中的kill杀死

但是这些方法直接简单粗暴,很不安全,而且也不推广

不使用stop的方法

之所以不安全不推广是因为:

  • stop方法不管线程逻辑是否完整,都会终止当前正在运行的线程
  • 会破坏其原子逻辑(多线程加了锁之解决资源共享,但是stop会将其所有锁丢弃,造成混乱)

1. 关闭线程

1.1 volatile关键字

使用自定义的标志位决定线程的执行情况

具体思路大致如下:设置一个 父线程 的状态变量,以其影响其子线程即可

public class test extends Thread {
    //标识线程是否结束
    public static boolean thread_stop = true;

    public void stopThread() {
        thread_stop = false;
    }

    public static void main(String[] args) {
        test t = new test();
        t.start();
        System.out.println("Father Thread Start");
        try {
            //先让线程跑起来
            Thread.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //结束线程

        //将其状态变量直接改为false
        //thread_stop = false;

        //调用方法改为false(与状态变量直接修改 一个道理)
        t.stopThread();

        System.out.println("Father Thread end");
    }

    @Override
    public void run() {
        while (thread_stop) {
            System.out.println("Child Thread Start");
        }
        System.out.println("Child Thread end");
    }
}

但是网上说不加volatile是停不下来的,其实是可以停下来的

只不过

加了volatile有几个好处:

  • volatile可以保证状态变量为系统内存值而不是缓存里值(避免值不一致)
  • volatile 关键字能够是该变量对其他线程“可见”,即当主线程修改变量并刷新到主内存后,会让其他线程去主内存中读取该变量。当然,volatile并不能保证线程的安全性

具体加在状态变量中的位置如下:

//标识线程是否结束
public static volatile boolean thread_stop = true;

之后具体完整的输出为:

具体完整的输出为:

Father Thread start
Child Thread Start
Child Thread Start
。。。
。。。
Child Thread Start
Child Thread Start
Father Thread end
Child Thread end

1.2 intrrrupt()方法

不能终止一个正在执行着的线程,它只是修改中断标志而已

这个方法分为两种情况:

线程处于阻塞:立马退出阻塞,抛出InterruptedException异常。通过捕获这个异常,来让线程退出

线程处于非阻塞:处于运行状态不受影响,仅仅标记了线程的中断为true。在适当的位置中调用isInterrupted方法查看是否被中断并且退出

public class test extends Thread {
    public static void main(String[] args) {
        test t = new test();
        t.start();
        System.out.println("Father Thread Start");
        try {
            //先让线程跑起来
            Thread.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //结束线程

        t.interrupt();

        System.out.println("Father Thread end");
    }

    @Override
    public void run() {
    //分配线程的中断状态,并且此状态可以由interrupted()方法生成
        while (!Thread.interrupted()) {
            System.out.println("Child Thread Start");
        }
        System.out.println("Child Thread end");
    }
}

执行结果截图:

具体完整的输出为:

Father Thread start
Child Thread Start
Child Thread Start
。。。
。。。
Child Thread Start
Child Thread Start
Father Thread end
Child Thread end

2.关闭线程池

优雅的关闭线程池:(比如ThreadPoolExecutor类)

可以通过shutdown方法逐步关闭池中的线程(温和安全)

shutdown():拒收新任务,不会立即终止线程池。而是要等所有任务缓存队列中的任务都执行完后才终止。

shutdownNow():拒收新任务,立即终止线程池。并尝试打断正在执行的任务。

并且清空任务缓存队列,返回尚未执行的任务

以下是对两个线程池关闭的方法源代码进行分析

而且关闭的途中,这两个方法也不是瞬间立马关闭,等待关闭的同时,还还调用awaitTermination方法来阻塞等待

2.1 shutdownNow()方法

查看java的源代码

在try内部结构中

1.检查其状态

2.原子性的修改线程池的状态为stop

3.遍历工作队列线程,调用interrupt方法

4.将队列中还未执行的放到任务队列

源码内部:其逻辑就是修改线程池状态为stop,工作队列中调用interrupt方法

在调用shutdownNow方法:

  • 正在执行的线程会(getTask返回null)导致线程退出。
  • 队列中读取的任务会阻塞,抛出异常之后。工作队列就会调用interrupt方法

2.2 shutdown()方法

同样也是看java的源代码

同样也是4步状态

1.检查其状态

2.修改线程池状态为SHUTDOWN

3.调用interruptIdleWorkers方法中断空闲线程(只有加锁成功的线程才会被调用interrupt方法)

而正在执行的线程是加锁失败,不会被中断

主要通过这个函数去区分判断

以上就是详解Java如何关闭线程以及线程池的详细内容,更多关于Java关闭线程 线程池的资料请关注我们其它相关文章!

(0)

相关推荐

  • java线程池使用及原理面试题

    目录 引导语 1.说说你对线程池的理解? 2.ThreadPoolExecutor.Executor.ExecutorService.Runnable.Callable.FutureTask 之间的关系? 3.说一说队列在线程池中起的作用? 4.结合请求不断增加时,说一说线程池构造器参数的含义和表现? 5.coreSize 和 maxSize 可以动态设置么,有没有规则限制? 6.说一说对于线程空闲回收的理解,源码中如何体现的? 7.如果我想在线程池任务执行之前和之后,做一些资源清理的工作,可以

  • java自带的四种线程池实例详解

    目录 java预定义的哪四种线程池? 四种线程池有什么区别? 线程池有哪几个重要参数? 如何自定义线程池 总结 java预定义的哪四种线程池? newSingleThreadExexcutor:单线程数的线程池(核心线程数=最大线程数=1) newFixedThreadPool:固定线程数的线程池(核心线程数=最大线程数=自定义) newCacheThreadPool:可缓存的线程池(核心线程数=0,最大线程数=Integer.MAX_VALUE) newScheduledThreadPool:

  • 学生视角手把手带你写Java 线程池

    目录 Java手写线程池(第一代) 手写线程池-定义参数 手写线程池-构造器 手写线程池-默认构造器 手写线程池-execute方法 手写线程池-处理任务 手写线程池-优雅关闭线程池 手写线程池-暴力关闭线程池 手写线程池-源代码 问题 Java手写线程池(第一代) 经常使用线程池,故今天突发奇想,手写一个线程池,会有很多不足,请多多宽容.因为这也是第一代的版本,后续会更完善. 手写线程池-定义参数 private final AtomicInteger taskcount=new Atomic

  • 模拟简单Java线程池的方法详解

    目录 一. 前言 二.线程池是什么? 三.线程池构造方法ThreadPoolExecutor的构造方法的参数都是啥意思? 四.模拟实现一个线程池 总结 一. 前言 为了实现并发编程,于是就引入了进程这个概念.进程就相当于操作系统的一个任务.多个进程同时执行任务,就实现了并发编程,能够更快的执行. 但是由于进程还不够轻量,创建一个进程,销毁一个进程消耗的资源不可忽视.如果进程数量不多的情况下,这些资源消耗是可以接受的,但是如果频繁的创建.销毁进程.就是一笔很大的开销了. 那要怎么办呢? 为了解决这

  • Java详解使用线程池处理任务方法

    什么是线程池? 线程池就是一个可以复用线程的技术. 不使用线程池的问题: 如果用户每发起一个请求,后台就创建一个新线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的,这样会严重影响系统的性能. 线程池常见面试题: 1.临时线程什么时候创建? 新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程. 2.什么时候会开始拒绝任务? 核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始任务拒绝. 1.线程池处理Runnable任务

  • 学生视角手把手带你写Java 线程池改良版

    目录 Java手写线程池(第二代) 第二代线程池的优化 线程池构造器 线程池拒绝策略 execute方法 手写线程池源码 MyExecutorService MyRejectedExecutionException MyRejectedExecutionHandle 核心类MyThreadPoolExecutor 线程池测试类 Java手写线程池(第二代) 第二代线程池的优化 1:新增了4种拒绝策略.分别为:MyAbortPolicy.MyDiscardPolicy.MyDiscardOldes

  • Java线程池的四种拒绝策略详解

    目录 预先配置 配置线程池. 创建线程任务 拒绝策略一:AbortPolicy 拒绝策略二:CallerRunsPolicy 拒绝策略三:DiscardPolicy 拒绝策略四:DiscardOldestPolicy 总结 dk1.5版本新增了 JUC 并发包,其中一个包含线程池. 四种拒绝策略:   拒绝策略类型 说明 1 ThreadPoolExecutor.AbortPolicy 默认拒绝策略,拒绝任务并抛出任务 2 ThreadPoolExecutor.CallerRunsPolicy

  • 详解Java如何关闭线程以及线程池

    目录 前言 1. 关闭线程 1.1 volatile关键字 1.2 intrrrupt()方法 2.关闭线程池 2.1 shutdownNow()方法 2.2 shutdown()方法 前言 这个问题是一个高频的面试题 而且在印象中是由stop方法执行或者终端中的kill杀死 但是这些方法直接简单粗暴,很不安全,而且也不推广 不使用stop的方法 之所以不安全不推广是因为: stop方法不管线程逻辑是否完整,都会终止当前正在运行的线程 会破坏其原子逻辑(多线程加了锁之解决资源共享,但是stop会

  • 详解Java多线程编程中的线程同步方法

    1.多线程的同步: 1.1.同步机制: 在多线程中,可能有多个线程试图访问一个有限的资源,必须预防这种情况的发生.所以引入了同步机制:在线程使用一个资源时为其加锁,这样其他的线程便不能访问那个资源了,直到解锁后才可以访问. 1.2.共享成员变量的例子: 成员变量与局部变量: 成员变量: 如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作,这多个线程是共享一个成员变量的. 局部变量: 如果一个变量是局部变量,那么多个线程对同一个对象进行操作,每个线程都会有一个该局部变量的拷贝.他们

  • 详解Java线程池如何实现优雅退出

    目录 shutdown()方法 shutdownNow()方法 awaitTermination(long, TimeUnit)方法 在[高并发专题]中,我们从源码角度深度分析了线程池中那些重要的接口和抽象类.深度解析了线程池是如何创建的,ThreadPoolExecutor类有哪些属性和内部类,以及它们对线程池的重要作用.深度分析了线程池的整体核心流程,以及如何拆解Worker线程的执行代码,深度解析Worker线程的执行流程. 本文,我们就来从源码角度深度解析线程池是如何优雅的退出程序的.首

  • 详解Java线程池如何统计线程空闲时间

    背景介绍 你刚从学校毕业后,到新公司实习,试用期又被毕业,然后你又不得不出来面试,好在面试的时候碰到个美女面试官! 面试官: 小伙子,我看你简历上写的项目中用到了线程池,你知道线程池是怎样实现复用线程的? 这面试官是不是想坑我?是不是摆明了不让我通过? 难道你不应该问线程池有哪些核心参数?每个参数具体作用是什么? 往线程池中不断提交任务,线程池的处理流程是什么? 这些才是你应该问的,这些八股文我已经背熟了,你不问,瞎问什么复用线程? 幸亏我看了一灯的八股文,听我给你背一遍! 我: 线程池复用线程

  • 详解Java线程池和Executor原理的分析

    详解Java线程池和Executor原理的分析 线程池作用与基本知识 在开始之前,我们先来讨论下"线程池"这个概念."线程池",顾名思义就是一个线程缓存.它是一个或者多个线程的集合,用户可以把需要执行的任务简单地扔给线程池,而不用过多的纠结与执行的细节.那么线程池有哪些作用?或者说与直接用Thread相比,有什么优势?我简单总结了以下几点: 减小线程创建和销毁带来的消耗 对于Java Thread的实现,我在前面的一篇blog中进行了分析.Java Thread与内

  • 详解java各种集合的线程安全

    线程安全 首先要明白线程的工作原理,jvm有一个main memory,而每个线程有自己的working memory,一个线程对一个variable进行操作时,都要在自己的working memory里面建立一个copy,操作完之后再写入main memory.多个线程同时操作同一个variable,就可能会出现不可预知的结果.根据上面的解释,很容易想出相应的scenario. 而用synchronized的关键是建立一个monitor,这个monitor可以是要修改的variable也可以其

  • 详解JAVA 线程-线程的状态有哪些?它是如何工作的?

    线程(Thread)是并发编程的基础,也是程序执行的最小单元,它依托进程而存在. 一个进程中可以包含多个线程,多线程可以共享一块内存空间和一组系统资源,因此线程之间的切换更加节省资源.更加轻量化,也因此被称为轻量级的进程. 线程的状态在 JDK 1.5 之后以枚举的方式被定义在 Thread 的源码中,它总共包含以下 6 个状态: NEW,新建状态,线程被创建出来,但尚未启动时的线程状态: RUNNABLE,就绪状态,表示可以运行的线程状态,它可能正在运行,或者是在排队等待操作系统给它分配 CP

  • 详解Java线程同步器CountDownLatch

    Java程序有的时候在主线程中会创建多个线程去执行任务,然后在主线程执行完毕之前,把所有线程的任务进行汇总,以前可以用线程的join方法,但是这个方法不够灵活,我们可以使用CountDownLatch类,实现更优雅,而且使用线程池的话,可没有办法调用线程的join方法的呀! 一.简单使用CountDownLatch 直接使用线程: package com.example.demo.study; import java.util.concurrent.CountDownLatch; public

  • 详解Java中的线程模型与线程调度

    JAVA线程模型 线程的实现主要有3种方式: 使用内核线程实现(1:1) 使用用户线程实现(1:N) 使用用户线程加轻量级进程实现(N:M) 使用内核线程实现(Kernel-Level Thread, KLT)(1:1) 内核线程就是直接由操作系统内核支持的线程,这种线程由内核来完成线程的切换,内核通过操作调度器对线程进行调度,并负责将线程的任务映射到各个处理器上. 程序一般不会直接去使用内核,而是去使用线程的一种高级接口--轻量级进程(Light Weight Process,LWP),轻量级

  • 详解Java 线程中断

    一.前言 大家肯定都使用过 Java 线程开发(Thread / Runnable),启动一个线程的做法通常是: new Thread(new Runnable( @Override public void run() { // todo sth... } )).start(); 然而线程退出,大家是如何做的呢?一般做法可能不外乎以下两种: 设置一个标志位:true / false 来退出: 强制退出:thread.stop:(我相信,现在应该没人会使用这种方式了,因为JDK也很早就废弃了该方法

随机推荐