【JavaEE初阶二】 Thread类及常见方法

1. 线程的创建

        主要来简单学习一下以下几种方法:

1.1 继承 Thread 类

        具体代码见前面的一章节,主体的步骤有以下几部分;

1、继承 Thread 来创建一个自定义线程类MyThread

class MyThread2 extends Thread{
    //重写run方法
    @Override
    public void run() {
        //run 方法就是该线程的入口方法
        while (true){
            System.out.println("hello thread,委婉待续");

            try {
                Thread.sleep(1000);//休眠1000ms
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

2、创建 MyThread 类的实例

3、调用 start 方法启动线程

1.2  实现 Runnable 接口

1、实现 Runnable 接口

class MyThread3 implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello runnable");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

        

        runnable 可以理解成“可执行的”,通过该接口,就可以抽象的表示出一端可以被其他实体来执行的代码;同时也可以简单的理解成runnable指的是重写的run方法的代码块;

2、创建 Thread 类实例, 且调用 Thread 的构造方法时将 Runnable 对象作为目标传递参数

代码讲解如下图所示:

3、调用 start 方法

        总体代码如下:继承Thread,重写run,但是使用匿名内部类

package thread;

class MyThread3 implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello runnable");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyThread3());
        t.start();

        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 1.3 使用匿名内部类

        内部类,在一个类里面定义的类~~匿名内部类最大的用途,没有名字意味着只能使用一次就无法生效了

1.3.1 匿名内部类创建 Thread 子类对象

        继承Thread,重写run

  代码如下:

// 使用匿名类创建 Thread 子类对象
Thread t1 = new Thread() {
	@Override
	public void run() {
		System.out.println("使用匿名类创建 Thread 子类对象");
	}
};

1.3.2 匿名内部类实现runnable,重写run

        代码如下:

 public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("使用匿名类创建 Runnable 子类对象");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t.start();

        总体来说,()里面的内容是thread父类构造方法的参数,代码部分其实是填写了runnable的匿名内部类的实例

1.3.3 lambda 表达式创建 Runnable 子类对象

        (函数式接口属于lambda背后的实现),相当于java在没被破坏原有的规则(方法不能脱离类,而单独存在)基础上,给了lambda一个合法的解释

        代码如下:

 public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (true) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

 2. Thread 类及常见方法

        Thread 类是 JVM 用来管理线程的一个类,即每个线程都有一个唯一的 Thread 对象与之关联。

       每个执行流,也需要有一个对象来描述,类似下图所示,而 Thread 类的对象 就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。

                 

2.1 Thread 的常见构造方法

        大体使用如下:

Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是当前线程的名字");
Thread t4 = new Thread(new MyRunnable(), "这是当前线程的名字");

        命名操作:

        可以通过使用 jconsole 命令观察线程时的看到自己的重命名线程名子

        代码如下:

package thread;

public class ThreadDemo7 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread,it's smallye");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "委婉待续001");

        // 在 start 之前, 设置线程为 后台线程 (不能在 start 之后设置)
       // t.setDaemon(true);

        t.start();
    }
}

        结果如下:

2.2 Thread 的几个常见属性

常见属性说明:

        1、ID 是线程的唯一标识,不同线程不会重复; Getid()是jvm自动分配的身份标识,会保证线程的唯一性

        2、名称是各种调试工具用到

        3、状态表示线程当前所处的一个情况;Getstate():进程有状态(就绪,阻塞),线程也有状态,java中对线程的状态,有进行了进一步的区分;

        4、优先级高的线程理论上来说更容易被调度到

        5、关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行;Isdaemon(demon:守护)判断是否是守护线程,也叫做“是否是后台线程”; 前台线程的运行会阻止进程结束,后台进程的运行不会阻止进程结束;

        代码举例:

public class ThreadDemo7 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ": 我还活着");
                    Thread.sleep(1 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + ": 我即将死去");
        },"委婉待续001");
        System.out.println(Thread.currentThread().getName()
                + ": ID: " + thread.getId());
        System.out.println(Thread.currentThread().getName()
                + ": 名称: " + thread.getName());
        System.out.println(Thread.currentThread().getName()
                + ": 状态: " + thread.getState());
        System.out.println(Thread.currentThread().getName()
                + ": 优先级: " + thread.getPriority());
        System.out.println(Thread.currentThread().getName()
                + ": 后台线程: " + thread.isDaemon());
        System.out.println(Thread.currentThread().getName()
                + ": 活着: " + thread.isAlive());
        System.out.println(Thread.currentThread().getName()
                + ": 被中断: " + thread.isInterrupted());
        thread.start();
        while (thread.isAlive()) {}
        System.out.println(Thread.currentThread().getName()
                + ": 状态: " + thread.getState());
    }
}

        结果展示:

package thread;

public class ThreadDemo7 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread,it's smallye");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "委婉待续001");
        // 在 start 之前, 设置线程为 后台线程 (不能在 start 之后设置)
       // t.setDaemon(true);

        t.start();
    }
}

        如上代码执行的时候,t持续执行,但是main已经结束了,结果如下:

        观察线程的状态:

        在idea中。没有出现下图情况时,我们的进程不算结束;

        咱们代码创建的t线程,默认就是前台线程,会阻止进程结束,只要前台线程没执行完,进程就不会结束,即时main已经执行完毕

        如下面的代码,我们将我们的线程设置为后台线程,他不会阻止我们的线程结束,故此有如下结果;

6、是否存活,即简单的理解,为 run 方法是否运行结束了;IsAliva:表示了内核中的线程pcb是否存在,Java代码中定义的线程对象(thread)实例虽然表示一个线程,但是对象本身的生命周期和内核中的pcb的生命周期是不一样的;

 2.3 启动一个线程-start()

        Thread类,使用一个start方法,来启动一个线程,对于同一个thread对象来说,只能调用一次。下图中的一个thread类对象被start启动两次,会有异常;

        运行结果如下所示:

        此时可以看到,另外一个线程依旧在运行 ;IlllegalThreadStateException,该异常是指非法的线程状态异常;即想要启动更多的线程,就是得创建新的对象。

        调用strart创建出新的线程(本质上是start会调用系统的api,来完成创建线程的操作)

 Q:下面的代码中关于start和run的区别,以及两者之间的关系?

package thread;

class MyThread4 extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadDemo11 {
    public static void main(String[] args) {
        Thread t = new MyThread4();
        t.start();
        // t.run();

        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

A:        1、在上述代码中,两个词语没有一点关系;

            2、详细分析如下图图解所示:

2.4  中断一个线程

        中断一个线程(终止一个线程)-->让线程run方法(入口方法)执行完毕---->核心就是让run方法能够提前结束----->非常取决于具体代码实现方式了

2.4.1 引入标志位

        代码如下:

        测试结果:

        通过上述代码引入标志位isquit,就可以让t线程结束掉,具体线程结束的时候却取决于在另外一个线程中何时修改isquit的值。  

        Main线程要想让t线程结束,其前提是t线程的代码,对于这样的逻辑有所支持,而不是t里的代码随便咋写都能够提前结束的。

2.4.2 标志位优化

        上述方法是手动设置变量,来进行结束线程。我们的优化是尝试让一个thread类,进行内置变量;代码解说图解如下:

         我们的线程中,如果没有sleep,interrupt可以让线程如我们所料的那样顺利结束,但是sleep引起了变数。刚才在执行sleep的过程中,调用interrupt,会大概率出现sleep休眠时间还没到,就被提前唤醒了。我们的线程被提前唤醒,会做以下两件事:

  1. 抛出interruptException(紧接着就会被catch获取到)
  2. 清除thread对象的isInterrupted标志位(通过interrupt方法,已经把标志位设置为true了,但是sleep提前唤醒,就把标志位又设回到false,所以此时循环还是会继续执行),如下情况:

        所以如果依旧想让线程按照原先的计划结束,我们需要在catch语句中添加break语句;

 代码如下所示:

package thread;

public class ThreadDemo13 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("我是一个线程, 正在工作中...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // e.printStackTrace();
                    // 加上 break , 此时抛出异常之后, 线程也会结束
                    break;
                }
            }
            System.out.println("线程执行完毕!");
        });

        t.start();

        Thread.sleep(3000);
        // 使用一个 interrupt 方法, 来修改刚才标志位的值
        System.out.println("让 t 线程结束");
        t.interrupt();
    }
}

 2.5 join()-等待一个线程

        多个线程的执行顺序是不确定的,虽然线程的底层的调度是无序的,但是我们可以在应用程序中。通过一些api来影响线程执行的顺序;所以我们引入了join方法,该方法能够应该线程执行的先后顺序;

        比如t1线程等待t0线程,则最后的结果是t0线程结束之后t1线程才会被执行;join会导致等待的那个线程处于阻塞的状态(开车如果1车被2车加塞,则2车需要等待,所以2车被阻塞

        接下来我们要知道使用join方法之后,关于谁等待谁的问题?具体图解如下图所示:

        深入分析:

        执行join的时候,接下来主要是看t线程是否在运行:

1、如果t在运行,main线程就会处于阻塞状态(t线程加入了,所以main线程就不去参加cpu的执行了)

2、如果t运行结束,main线程就会从阻塞中恢复过来,继续向下执行(加塞的前面的那个车走了,我的车就需要继续执行我的任务了,总之导致我的任务结束的比前面的车晚)

        综上所述,是由于阻塞,是这两个线程的结束时间产生了先后关系;

ps:本次的内容就到这里了,如果大家感兴趣的话就请一键三连哦!!!

我的idea的背景图是刘姝贤,如果感兴趣的话关注b站up主刘景灵000!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VScode远程连接服务器,Pycharm专业版下载及远程连接(深度学习远程篇)

Visual Code、PyCharm专业版&#xff0c;本地和远程交互。 远程连接需要用到SSH协议的技术&#xff0c;常用的代码编辑器vscode 和 pycharm都有此类功能。社区版的pycharm是免费的&#xff0c;但是社区版不支持ssh连接服务器&#xff0c;只有专业版才可以&#xff0c;需要破解…

xlua源码分析(四) lua访问C#的值类型

xlua源码分析&#xff08;四&#xff09; lua访问C#的值类型 上一节我们主要探讨了C#是如何使用interface和delegate访问lua层的table和function的&#xff0c;本节我们跟着Examples 05_NoGc&#xff0c;来看看xlua是如何实现lua层无gc访问C#的值类型的。 首先例子中用到的lua…

在线快速获取UDID教程

第一步 进入UDID 登录咕噜分发官网(https://www.gulufenfa.com) 点击工具箱 进入控制台—【开发者工具】—【UDID】 第二步 获取UDID 进入UDID快速获取页面 因需要历史UDID所以需要登录您在咕噜分发的账号 用苹果手机扫描二维码根据操作UDID 第三步 手机操作教程 手机…

科技云报道:开源才是大模型的未来?

科技云报道原创。 一年前&#xff0c;ChatGPT横空出世&#xff1b;7个多月后&#xff0c;Meta宣布开源LLaMA 2&#xff0c;并且可免费商用。 这一天&#xff0c;也成为大模型发展的分水岭。短时间内&#xff0c;LLaMA 2对一些闭源的大模型厂商造成了致命性的打击。 随后&…

CGAL的三角曲面网格骨架化

1、介绍 马模型的曲线骨架。 骨架是用于分割、形状匹配、重建、虚拟导航等的有效形状抽象。顾名思义&#xff0c;曲线骨架是曲线结构的图&#xff08;1D&#xff09;。对于3D几何体来说&#xff0c;它不是由表面&#xff08;2D&#xff09;组成的中轴。如图所示&#xff0c;形…

VMvare虚拟机之文件夹共享防火墙设置

目录 一.jdk的配置&TomCat的配置 1.1 jdk配置 1.2 tomcat配置 二.文件夹共享功能 2.1 作用 2.2.高级共享和普通共享 三.防火墙设置 3.1 入站规则和出站规则 四.附图-思维导图 一.jdk的配置&TomCat的配置 建立一个共享文件夹&#xff0c;将jdk文件和tomcat文…

嵌入式——RTC内置实时时钟

学习目标 理解原理图RTC设计部分掌握初始化RTC掌握设置时间掌握读取时间学习内容 RTC原理图 RTC结构框图 RTC时钟 开发流程 加载依赖。gd32f4xx_rtc.c,gd32f4xx_pmu.c初始化RTC。时钟配置。获取时钟。RTC初始化 // 电池管理加载 rcu_periph_clock_enable(RCU_PMU); pmu_back…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Progress进度条组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Progress进度条组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Progress组件 进度条也是UI开发最常用的组件之一&#xff0c;进度条组件…

分类预测 | Matlab实现SCSO-SVM基于沙猫群优化算法优化支持向量机的多变量分类预测【23年新算法】

分类预测 | Matlab实现SCSO-SVM基于沙猫群优化算法优化支持向量机的多变量分类预测【23年新算法】 目录 分类预测 | Matlab实现SCSO-SVM基于沙猫群优化算法优化支持向量机的多变量分类预测【23年新算法】分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现SCSO-…

docker部署kafka zookeeper模式集群

单机模式链接&#xff1a;https://blog.csdn.net/wsdhla/article/details/133032238 kraft集群模式链接&#xff1a;部署Kafka_kafka 部署-CSDN博客 zookeeper选举机制举例&#xff1a; 目前有5台服务器&#xff0c;每台服务器均没有数据&#xff0c;它们的编号分别是1,2,3,4,5…

如何通过内网穿透实现远程访问本地Linux SVN服务

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

Nature | 大型语言模型(LLM)能够发现和产生新知识吗?

大型语言模型&#xff08;LLM&#xff09;是基于大量数据进行预训练的超大型深度学习模型。底层转换器是一组神经网络&#xff0c;这些神经网络由具有自注意力功能的编码器和解码器组成。编码器和解码器从一系列文本中提取含义&#xff0c;并理解其中的单词和短语之间的关系。通…

python 通过opencv及face_recognition识别人脸

效果&#xff1a; 使用Python的cv2库和face_recognition库来进行人脸检测和比对的 0是代表一样 认为是同一人。 代码&#xff1a; pip install opencv-python pip install face_recognition# 导入cv2库&#xff0c;用于图像处理 import cv2 # 导入face_recognition库&#…

跨域 - CORS跨域资源共享介绍

目录 1&#xff0c;介绍2&#xff0c;简单请求判定交互规范 3&#xff0c;非简单请求交互规范1&#xff0c;发送预检请求2&#xff0c;预检请求响应3&#xff0c;浏览器发送真实的请求&#xff0c;服务器完成真实的响应。 附带身份凭证 相关内容&#xff1a; 浏览器同源策略和跨…

C# WPF上位机开发(MySql访问)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们学习了数据库sqlite&#xff0c;不过这是一种小型的数据库&#xff0c;作为内部使用还可以。但是&#xff0c;如果要与外面的其他供应商进…

doris数据模型,06-Aggregate(聚合模型)

聚合模型的特点 将表中的列分为Key和Value。 Key是数据的维度列&#xff0c;比如时间&#xff0c;地区等等。key相同时会发生聚合。 Value是数据的指标列&#xff0c;比如点击量&#xff0c;花费等等。 每个指标列还会有自己的聚合函数&#xff0c;如&#xff1a;sum&#xff…

路由器常见故障分析及处理方法!

对当前的大多数网络来说&#xff0c;无论是实现网络互连还是访问Internet&#xff0c;路由器是不可或缺的。 由于路由器的重要性&#xff0c;对它的管理就成了维护人员的日常工作中重要的一部分&#xff0c;而路由器的故障分析和排除也是令许多维护人员极为困扰的问题之一。 路…

蓝牙物联网在智慧医疗中的应用

物联网技术开启了万物互联的时代&#xff0c;并且随着智慧城市建设的加速推进及物联网技术对各行业的逐步渗透&#xff0c;“智慧”概念应运而生&#xff0c;诸如智慧能源、智慧交通、智慧医疗等“遍地开花”&#xff0c;可以说&#xff0c;物联网技术给各行业带来了产业模式上…

asp.net core 教程

asp.net core 教程 写在前面新建项目Get和PostGETPOST MVC-模型控制视图如何通俗理解MVC代码实例 API模型&#xff08;前后端分离&#xff09;前端代码后端代码 文件配置优先级优先级顺序 从数据库读取配置文件数据缓存 写在前面 学了快一年多的C#了&#xff01; 我最开始学的…

【数据结构入门精讲 | 第五篇】栈知识点及考研408、企业面试练习

在上一篇中我们进行了表的专项练习&#xff0c;在这篇文章中我们将介绍栈的相关知识点。 目录 基础概念顺序栈链栈判断题选择题填空题函数题R6-1 在一个数组中实现两个堆栈 编程题R7-1 汉诺塔的非递归实现R7-2 表达式转换R7-3 出栈序列的合法性R7-4 包装机R7-1 彩虹瓶 基础概念…