【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)

news2024/11/13 15:31:08

阿华代码,不是逆风,就是我疯,你们的点赞收藏是我前进最大的动力!!7000字长文,希望本文内容能够帮助到你!

目录

一:创建线程五种方式

方式一:继承Thread类,再实例化

 方式二:实现Runnable接口,重写run方法

方式三:匿名内部类写法

方式四:Runnable+匿名内部类

方式五:lambda表达式

二:Thread常见方法

1:Thread方法

2:获取Thread属性的方法 

(1)守护进程

(2).setDaemon()方法

①前台/后台线程

②.setDaemon

③结果分析:

(3)isAlive()

(4)start方法和run方法的区别

三:如何提前终止一个线程

1:标志位——isQuit

(1)变量捕获:重点重点重点

(2)lambda复制和传参理解

2:Thread内置变量isinterrupted

(1)isinterrupted本质

(2)sleep清空标志符

(3)解决方式(catch中加代码)


一:创建线程五种方式

方式一:继承Thread类,再实例化

package thread;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Hua YY
 * Date: 2024-09-17
 * Time: 14:20
 */
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("这就是进入该线程的入口");
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        //根据类,创建实例,线程实例才是真正的线程
        //一般用向上转型的写法
        Thread t = new MyThread();
        t.start();

    }
}

 方式二:实现Runnable接口,重写run方法

package thread;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Hua YY
 * Date: 2024-09-19
 * Time: 9:20
 */
class MyTread3 implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("这里是run线程");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class TreadDome3 {
    public static void main(String[] args) {
        Runnable runnable = new MyTread3();//相当于借刀杀人了
        Thread t = new Thread(runnable);
        t.start();
        while(true){
            System.out.println("这里是main函数线程");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }


}

注:可以通过Runnable这个接口,可以抽象出一段被其他实体执行的代码,还是需要搭配Thread类来进行使用

方式三:匿名内部类写法

在实例化Thread对象时{}里创建匿名内部类,重写run方法

匿名内部类:

①没有名字,不能重复使用

②这个新的类继承自Thread,并且{}里可以定义子类的属性和方法

③t的指向不仅仅是Thread,还有它的子类(虽然没名字),(即不仅是Thread的实例,也是其子类的实例)

package thread;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Hua YY
 * Date: 2024-09-19
 * Time: 9:39
 */
public class ThreadDome4 {
    public static void main(String[] args) {
        Thread thread = new Thread(){
          public void run(){
              while(true){
                  System.out.println("这里是由匿名内部类实现的线程");
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
              }
          }
        };
        thread.start();


        while(true){
            System.out.println("这里是main函数入口");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

方式四:Runnable+匿名内部类

实现Runnable接口,重写run方法实现匿名内部类

package thread;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Hua YY
 * Date: 2024-09-19
 * Time: 10:07
 */
public class ThreadDome5 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("run method");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        thread.start();

        while(true){
            System.out.println("main method");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }

}

方式五:lambda表达式

lambda表达式,适合简单直观的代码,如果代码过于复杂还是需要提取出来的

在Java中方法的实现依赖于类,方法不能脱离类单独存在,这里就导致为了设置回调函数,不得不套上一层类,但是并不常用——引出了lambda表达式。

函数式接口相当于在没有破坏java原有的规则上(方法不能脱离类单独存在),单独给lambda一个解释

第一个标记的红色方框中的()->  ,()括号中可以带参数

package thread;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Hua YY
 * Date: 2024-09-19
 * Time: 10:29
 */
public class ThreadDome55 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            while (true){
                System.out.println("run method");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        thread.start();
        while(true){
            System.out.println("main method");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }


}

二:Thread常见方法

1:Thread方法

2:获取Thread属性的方法 

后面的代码中会运用到,有相应的解释

(1)守护进程

通过之前的学习,我们知道,main方法走完了,整个进程就结束了。 现在我们引入了线程的概念,那这个结论还准确吗。有没有例外呢? 

下面我们举例:代码里面我们创建main函数线程

这里我们使用jconsole工具来辅助(上一篇博客有讲怎么使用jconsole前面)

package thread;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Hua YY
 * Date: 2024-09-19
 * Time: 10:58
 */
public class ThreadDome6 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("run method");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        } , "这个线程的名字叫:测试");

        thread.start();

    }

}

我们可以发现jconsole线程运行详细信息中,没有main函数,但是有我们的测试线程很明显即使main函数这个前台线程已经结束了,但是这个进程依旧还在运行(可以说是这个“这个线程的名字叫:测试”的线程还在运行)

由此我们引出一组概念:前台线程和后台线程

(2).setDaemon()方法

①前台/后台线程

重点:咱们创建的线程,默认都是前台线程,会阻止进程的结束,只要前台线程没有执行完,进程就不会结束,即使main函数这个线程已经执行完毕

(这么理解:后台进程没有话语权,只要还有前台线程,进程就不会结束)

②.setDaemon

理解:Daemon是守护的意思,这个方法是把某线程设置为后台线程(守护线程)

注:这个方法一定要在“.start”方法之前设置

接上文,我们引入.setDaemon()方法,可以将Thread下的线程从前台改为后台,下面会详细解释两者区别。

③结果分析:

什么都没有打印是因为,我们把main函数设置为了后台线程,这个线程走完了,那整个进程都结束了,Thread这个前台线程

(3)isAlive()

isAlive(),方法,表示了当前内核中的线程(pcb)是否还存在,内核中的线程和实例的周期是不一致的

①在创建完Thread对象之后,start方法之前,pcb还没有创建出来,所以答疑结果为false

②在start方法之后,线程结束之前,对象存在,pcb存在,打印结果为true,

③线程run完了,pcb得到释放,为false

④注意下面的代码Thread.sleep(5000) ,如果沉睡时间改为3000ms以下,那么最后一条打印结果就不确定了,线程的随机调度

package thread;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Hua YY
 * Date: 2024-09-19
 * Time: 18:24
 */
public class ThreadDemon8 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        System.out.println(thread.isAlive());//false,有对象,无pcb线程
        thread.start();//创建线程
        System.out.println(thread.isAlive());//true,有对象,有pcb
        Thread.sleep(5000);//main函数线程沉睡5s,此时Thread线程3s沉睡完了,thread线程结束,thread对象还在
        System.out.println(thread.isAlive());//false

    }

}

(4)start方法和run方法的区别

start方法核心是创建一个新的线程,但是run方法仅仅是一个方法、一个线程的入口,类似main方法的入口,两者不能说没有关系,简直是毫无关系!!!

package thread;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Hua YY
 * Date: 2024-09-19
 * Time: 18:56
 */
public class ThreadDemon9 {
    public static void main(String[] args) {
        Thread t1 = new Thread(){
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("run线程方法正在执行");
                }
            }
        };
        //t1.run();//不屏蔽这行代码,将会一直死循环在run方法中,单线程main的
        t1.start();
        System.out.println("main函数线程正在执行");
    }

}

三:如何提前终止一个线程

1:标志位——isQuit

我们需要在Thread实例化中写符合能够控制线程的结构,下面的代码我们引入isQuit这个静态成员变量,来控制while循环,进而达到提前终止线程这样一个目的

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Hua YY
 * Date: 2024-09-19
 * Time: 19:18
 */
public class ThreadDemon12 {
    private static boolean isQuit = false;
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            while(!isQuit){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("t1线程正在运行");
            }
        });
        t1.start();
        Thread.sleep(2001);
        isQuit = true;
        System.out.println("终止t1线程");
    }

}

(1)变量捕获:重点重点重点

提问:上述代码能否将isQuit这个变量在main方法中定义(局部变量)。不行

在lambda匿名内部类当中有一条规定:lambda匿名内部类可以获取外面定义的成员变量(本质是将外面的成员变量作为参数传进来,这个被捕获的(变量)参数必须是final,或者“事实final”)

大前提这个变量定义在main函数内部

零:解释“事实final”

定义的成员变量虽然没有被final修饰,但是这个成员变量没有被修改过(有点那种“虽无夫妻之名,但有夫妻之实”的感觉)。

举例1:在mian函数内部定义  不加final修饰   的局部变量(可行)

举例2:在mian函数内部定义  不加final修饰  的局部变量  并且修改isQuit的值(不可行,lambda报错)

举例3:在mian函数内部定义  加final修饰  的局部变量  并且修改isQuit的值(不可行,final报错)

(2)lambda复制和传参理解

承接举例1:我们删除isQuit这一行代码

我们都知道,main函数执行完毕,那么main函数中定义的局部变量会进行回收,但是在上述两个线程中,main方法结束了,isQuit已经被回收了,这个Thread(t1)线程为什么还能运行(他线程用到了isQuit作为循环判断条件)。

总结:这里是因为,lambda获得的成员变量,是作为参数传进来的(可以理解为复制拷贝,是一个全新的个体),main方法执行完了,isQuit的栈帧就销毁了,但是Thread(t1)中拷贝过来的isQuit所创建的栈帧还在呀!!!

2:Thread内置变量isinterrupted

(1)isinterrupted本质

利用Thread类的实例内部成员变量,来取代我们在1中使用的,手动创建的静态成员变量isQuit

package thread;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Hua YY
 * Date: 2024-09-20
 * Time: 10:23
 */
public class ThreadDemon13 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
           while(!Thread.currentThread().isInterrupted()){ //当t1.interrupt();生效,循环条件为false,被打断了
               try {
                   Thread.sleep(500);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               System.out.println("t1线程正在运行");
           }
        });

        t1.start();
        Thread.sleep(2001);
        t1.interrupt();//让t1被打断,相当于isQuit = true
        System.out.println("终止t1线程");

    }
}

 (2)sleep清空标志符

结果分析——

目的:给程序员创造更多的“可操作空间”

运行上述代码,报错,这是因为,我们的中断了t1线程的sleep提前唤醒会做两件事:1:清空标志符,2:抛出InterruptedException异常),sleep把interrupted又设置回false,进而while循环判定条件变成true,最后被catch捕获中断异常。

(3)解决方式(catch中加代码)

①让线程立即结束:加上break;

②让线程不结束:不加break,让它报错

③让线程执行一些代码后再结束:写一些其它代码后,再break。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2151853.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

服务器非法关闭后MySQL服务启动失败

在写这篇文章前,我弄好了,写完之后把成功安装的几个MySQL都删除了,只留了最后测试成功的服务“mysql-test” ,然后点击运行,发现又出现上图的错误。心态炸了。 本以为定位到问题了,但是这个错误让我迷茫了。我只能临时…

缓存技巧 · Spring Cache Caffeine 高性能缓存库

Caffeine 背景 Caffeine是一个高性能的Java缓存库,它基于Guava Cache进行了增强,提供了更加出色的缓存体验。Caffeine的主要特点包括: 高性能:Caffeine使用了Java 8最新的StampedLock乐观锁技术,极大地提高了缓存的并…

VisionPro - 基础 - 00 模板匹配技术和在VP中的使用 - PMAlign - PatMax - (3)

前言: 针对PatMax 的高级应用和原理,在这一节继续进行说明:这一节主要考虑的是PatMax模板匹配的原理: How PatMax Finds Patterns in an Image PatMax 模板匹配原理 1 Run-time Space When you search for a PatMax pattern in …

World of Warcraft [CLASSIC] International translation bug

internationalization i18n_getinternationalizationjs-CSDN博客 1)国际化翻译不完整 Chance on melee and ranged critical strike to increase your attack power by 1262 for 10s. 2)更新美酒节,服务器并发太高,被提出副本 Wo…

DataFrame生成excel后为什么多了一行数字

问题描述 python查询数据生成excel文件,生成的excel多了第一行数字索引,1,2,3,4,5...... 代码: df pd.DataFrame(data)df.to_excel(filename, sheet_name用户信息表, indexFalse) 解决: 原理也很简单,就是设置个参…

Java对象一口气讲完!φ(* ̄0 ̄)

Java Object类 Java面向对象设计 - Java Object类 Java在java.lang包中有一个Object类。 所有Java类都直接或间接扩展Object类。 所有Java类都是Object类的子类Object类是所有类的超类。 Object类本身没有超类。 Object类的引用变量可以保存任何类的对象的引用。 以下代…

python中ocr图片文字识别样例(一)

一、使用easyocr安装依赖 pip install easyocr pip install opencv-python-headless # 处理图像二、具体实现,此处有个缺陷,大家可以尝试解决下,识别的文字打印结果没问题,但是图片识别出现乱码: 2.1 具体识别的图片…

Springboot 文件上传下载相关问题

文章目录 关于Springboot 文件上传下载问题解决方案注意事项文件上传文件下载文件删除文件在线打开在写练习的时候,发现了一些小小的问题,已经在 上述代码中体现。① 代码路径碰到中文的时候,会有乱码,需要转换(内容中…

【全网首发】2024华为杯数学建模ABCDEF选题方向+完整思路代码+数据集处理+可视化结果

2024华为杯研究生数学建模比赛ABCDEF选题分析 建议选哪道题? 点击链接加入群聊【2024华为杯数学建模助攻资料】:http://qm.qq.com/cgi-bin/qm/qr?_wv1027&kxtS4vwn3gcv8oCYYyrqd0BvFc7tNfhV7&authKeyedQFZne%2BzvEfLEVg2v8FOm%2BWNg1V%2Fiv3H…

KMP算法的实现

这是C算法基础-数据结构专栏的第二十六篇文章,专栏详情请见此处。 引入 KMP算法是一种可以快速查找某一字符串在一个文本中的所有出现的算法。 下面我们就来讲KMP算法的实现。 定义 Knuth–Morris–Pratt 算法,简称KMP算法,是由Knuth、Pratt…

2024华为杯数学建模竞赛E题

2024年中国研究生数学建模竞赛E题 高速公路应急车道紧急启用模型 高速公路拥堵现象的原因众多,除了交通事故外,最典型的就是部分路段出现瓶颈现象,主要原因是车辆汇聚,而拥堵后又容易蔓延。高速公路一些特定的路段容易形成堵点&…

云手机的便捷性和安全性体现在哪?

随着5G技术的迅速发展,云手机在游戏、电商以及新媒体营销等领域中的应用日益广泛。它不仅能够显著降低成本、提升效率,还随着边缘计算和云技术的进步,展现出无限的增长潜力。 云手机的便捷性体现在哪里? 云手机的便捷性毋庸置疑。…

煤矿智慧矿井数据集 (1.煤矿采掘工作面智能分析数据集2.煤矿井下钻场智能分析数据集 )

智慧矿井智能分析数据集 数据1:数据1包含煤矿采掘工作面工人安全帽检测,工人行为检测(行走,站立,坐,操作,弯腰,靠,摔,爬),液压支撑防护…

C++ | Leetcode C++题解之第421题数组中两个数的最大异或值

题目: 题解: struct Trie {// 左子树指向表示 0 的子节点Trie* left nullptr;// 右子树指向表示 1 的子节点Trie* right nullptr;Trie() {} };class Solution { private:// 字典树的根节点Trie* root new Trie();// 最高位的二进制位编号为 30static…

en造数据结构与算法C# 用Unity实现简单的群组行为算法 之 对齐

en造数据结构与算法C# 用Unity实现简单的群组行为算法 之 聚集-CSDN博客 en造数据结构与算法C# 用Unity实现简单的群组行为算法 之 聚集-CSDN博客 演示 思路 1.检测 自然是沿用前两节的检测范围 2.对齐朝向 对齐朝向就是邻居鸟的forward加起来再除总数得到平均数 3.对齐…

企业急于采用人工智能,忽视了安全强化

对主要云提供商基础设施上托管的资产的安全分析显示,许多公司为了急于构建和部署 AI 应用程序而打开安全漏洞。常见的发现包括对 AI 相关服务使用默认且可能不安全的设置、部署易受攻击的 AI 软件包以及不遵循安全强化指南。 这项分析由 Orca Security 的研究人员进…

关于机器学习和深度学习的区别有哪些?

成长路上不孤单😊😊😊😊😊😊 【14后😊///C爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于机器学习和深度学习区别的相关内容&…

Altium Designer(AD)百度云下载与安装(附安装步骤)

在我们日常使用当中,Altium designer常常也被简称为AD,是一款一体化的电子产品开发系统软件,主要运行在Windows操作系统上。 我们通过Altium designer把原理图设计、电路仿真、PCB绘制编辑、拓扑逻辑自动布线、信号完整性分析和设计输出等技…

企业为什么要做算法备案?

企业为什么要做算法备案? 在数字经济迅速发展的今天,算法已成为推动各行各业创新发展的核心动力。鉴于此,国家层面正积极构建完善的监管框架,旨在促进算法应用的健康发展,确保其在法治轨道上运行。 近期,一…

揭开数据能力的神秘面纱

在当今数字化时代,数据已成为企业和组织的重要资产。拥有强大的数据能力,能够帮助企业更好地理解市场、客户和业务,从而做出更明智的决策。然而,数据能力究竟是什么?它包含哪些方面?又如何提升呢&#xff1…