Javaee---多线程(一)

在这里插入图片描述

文章目录

  • 1.线程的概念
  • 2.休眠里面的异常处理
  • 3.实现runnable接口
  • 4.匿名内部类子类创建线程
  • 5.匿名内部类接口创建线程
  • 6.基于lambda表达式进行线程创建
  • 7.关于Thread的其他的使用方法
    • 7.1线程的名字
    • 7.2设置为前台线程
    • 7.3判断线程是否存活
  • 8.创建线程方法总结
  • 9.start方法
  • 10.终止(中断)线程-interrupt
    • 10.1自定义标志位
    • 10.2使用系统里面的标志位
  • 11.线程等待join
  • 12.线程方法总结
  • 11.线程等待join
  • 12.线程方法总结

1.线程的概念

(下面的这个图片里面的代码有错误,我们new的这个mythread类的对象,应该是使用这个Thread这个类进行接收,这个是个向上转型的过程);

  • 我们的这个里面使用到了这个thread类,但是不需要进行这个import的操作,这个主要就是因为我们的这个Thread是位于这个java.lang这个包下面的;像我们之前使用的这个String也是在这个包下面,我们也没有进行这个import的操作;
  • 这个Thread里面是有这个run方法的,我们的这个mythread类里面对于这个夫类里面的方法进行了重写;
  • 我们的main方法里面执行这个start时候,就会进入这个run入口进而对于这个内容进行打印输出;

image-20241019192827539

实际上打印输出的时候main方法里面的内容和我们的自定义类里面的这个内容是独立进行的,两个并不会相互依赖;每一个线程都是一个独立的执行流;

image-20241019211712898

当我们把这个start修改为这个run之后,这个就会先去执行我们自己的自定义类里面的这个重写的run方法,这个执行完成之后才会继续执行这个下面的helli main打印输出,但是因为我们的这个是死循环,因此这个会一直打印输出我们的hello thread;

image-20241019211737878

2.休眠里面的异常处理

为了更加清楚的看到这个打印的效果,我们可以对于这个程序进行休眠操作,就是使用这个sleep方法,但是这个方法在进行使用的时候可能会出现一些问题,就是异常情况,针对于自定义类和main方法里面的异常,我们的处理手段是不一样的:

下面的这个异常是受查异常,需要进行显示处理:要么throws,要么try-catch

重写父类的方法,父类的方法里面没有throws,因此我们不可以throws;

image-20241020212336753

image-20241020212513823

相比之下,在这个main里面的这个内容都是我们自己写的,因此这个时候我们就可以使用这个throws方法对于这个出现的异常进行抛出;

image-20241020213545037

有了上面的两个休眠的操作,这个时候我们重新运行这个程序,这个时候就会发现这个两个语句的打印输出的速度就变慢了,但是这个出现的先后顺序其实并不是确定的,这个主要取决于我们的操作系统对于这个线程的调度(调度器)的具体实现,因此我们可以看到有的时候是这个hello thread先打印输出,但是有的时候是这个hello main先打印输出;

3.实现runnable接口

下面的这个就是实现我们的这个runnable接口的方式,两个类里面的这个异常的处理方式还是一样的,一个是捕捉异常,一个就是抛出异常;

但是在这个主方法里面,我们使用这个已有的MyRunnable类实例化对象,相当于是定义了一个任务,然后我们吧这个传递到我们的这个new Thread这个参数里面去,相当于就是使用这个Thread去执行这个任务,其他的和上面的这个方式是没有任何区别的;

针对于这个方式,实现了这个runnable接口的这个类的实例化对象作为我们的这个Thread接口的构造方法的参数,这个和上面最大的不同就是解耦合:耦合指的就是我们的一个程序里面的不同模块之间的影响的程序,解耦合就是解除不同的模块之间的相互的影响;

这样的解耦合的好处就是我们只是定义了这个任务,但是交给谁去执行这个任务就没那么重要了,这个就是这个实现接口的方式和上面的最大不同;
在这里插入图片描述

4.匿名内部类子类创建线程

public class Test {
    public static void main(String[] args) throws InterruptedException {
        //匿名内部类,不知道这个类叫什么,但是知道这个类是我们的Thread的子类
        //不知道这个类的名字也不重要,因为这个类我们只会使用一次
        
        //下面的这个new后面的这个其实就可以理解为是一个类,使用这个没有名字的类实例化对象,在这个匿名的类里面对于这个方法进行重写,里面的这个对于异常的处理和定义两个类的时候是一样的;
        Thread t = new Thread(){
            @Override
            public void run() {
                while(true){
                    System.out.println("hello thread!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        
        //使用我们创建的这个线程去开始这个线程的执行,start就是这个线程执行的入口
        t.start();
        while(true){
            System.out.println("hello main!");
            Thread.sleep(1000);
        }

    }
}

5.匿名内部类接口创建线程

public class Test {
    public static void main(String[] args) throws InterruptedException {
        //匿名内部类,不知道这个类叫什么,但是知道这个类是我们的Thread的子类
        //不知道这个类的名字也不重要,因为这个类我们只会使用一次
        //下面的这个就是我们不知道这个类叫什么名字,但是这个类实现了这个接口Runnable
        //因此在这个匿名内部类里面对于这个接口里面的方法进行了重写
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("hello thread!");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };

        //这个就是我们的这个实现接口的类的实例化对象作为一个参数进行传递
        Thread t=new Thread(runnable);
        t.start();
        while(true){
            System.out.println("hello main!");
            Thread.sleep(1000);
        }

    }
}

6.基于lambda表达式进行线程创建

lambda实际上是这个匿名内部类的一个简化的方式:本质上就是一个函数表达式

public class test {
    public static void main(String[] args) throws InterruptedException {
        //lambda表达式是一个匿名函数,主要是实现回调函数的效果
        //回调函数是计算机里面的一个很重要的术语
        //回调函数--函数指针:函数指针实现转移表,减少if-else分支数目
        //使用函数指针作为回调函数--实现qsort()的比较
        
        //其实这个lambda表达式并不是很复杂,形式上就是这个()->{},我们的这个相关的代码就是写在这个大括号			里面的
        Thread t = new Thread(()->{
            while(true){
                System.out.println("hello thread!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t.start();
        while(true){
            System.out.println("hello main!");
            Thread.sleep(1000);
        }

    }
}

7.关于Thread的其他的使用方法

上面我们介绍两个:

1.直接调用无参数的,就是我们最开始介绍的;

2.传递的参数是我们的实现接口的类的实例化对象;

除此之外,我们还可以传递name参数,给这个线程起名字,对于线程进行区分:

7.1线程的名字

public class Test {
    public static void main(String[] args) {
        //String name;
        Thread t = new Thread(() -> {
            while(true){
                System.out.println("hello thread!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"这个是新的线程" );
        //上面的这个就是相当于是在原来的这个表达式的后面多了这个name属性,这样我们使用这个jconsole工具			进行查看的时候,我们就可以看到自己对于这个线程的命名了;

        t.start();
    }
}

7.2设置为前台线程

还是上面的这个程序,其实在这个默认的情况下,这个线程就是前台线程,就是我们的这个线程不结束,这个程序就不会结束,但是我们的后台线程就是这个线程结束与否,对于我们的整个程序的结束没有影响;

这个默认情况下是前台线程,我们可以使用这个setDaemon方法把这个线程设置为后台的线程;

我们的这个具体的差别可以在打印的时候看出来,就是我们的这个内容好像没有进行打印,这个程序就结束了;

public class Test {
    public static void main(String[] args) {
        //String name;
        Thread t = new Thread(() -> {
            while(true){
                System.out.println("hello thread!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"这个是新的线程" );

        //设置为后台的线程,运行起来就不会进行打印
        t.setDaemon(true);

        t.start();
    }
}

7.3判断线程是否存活

这个就是使用的t.isAlive方法判断这个线程是不是存活的,下面的这个使用start开始的时候,就是存活的线程,打印输出的结果就是true,但是休眠之后,这个就是死亡的,打印输出false;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            System.out.println("线程开始");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程结束");
        });
        t.start();
        System.out.println(t.isAlive());
        Thread.sleep(2000);
        System.out.println(t.isAlive());
    }
}

8.创建线程方法总结

image-20241021125053812

9.start方法

start和run方法的区别:

我们的start方法会去调用这个系统里面的api,进行线程的创建;

run只会去执行这个线程里面的内容(会在这个start创建线程之后自动被调用);

两个的本质区别就是是不是调用了这个系统里面的api,创建出来了新的线程;

10.终止(中断)线程-interrupt

10.1自定义标志位

需要注意的就是这个自定义的静态的变量需要是全局的,不可以是局部的,因为这个lambda是变量的捕捉,这个变量需要是这个final或者是视为这个final的变量;

我们的这个变量一旦写作这个局部的,这个时候我们下面不可以进行修改了,但是我们的有修改,所以写成局部的就会报错,大家可以自己去尝试一下,看看这个效果,这个主要是lambda的语法导致的;

image-20241021132639605

10.2使用系统里面的标志位

为什么会有下面的这个,因为上面的存在缺陷,就是我们需要手动创建,而且我们的线程如果正在休眠,我们把这个标志位修改掉,这个线程无法及时的做出回应,或者是叫做响应;

但是如果我们使用这个系统里面的标志位,这个就是currentThread().isInterrupted()方法进行判断当前的这个线程是不是被打断了;

我们的这个里面有一个t.sleep()这个方法就是进行线程的唤醒,上面的这个手动创建标志位的方法,如果我们的这个线程在休眠,这个时候不可以及时的进行响应,但是我们的这个interrupt这个方法,就会触发这个线程的异常,让这个休眠的线程提前被唤醒,但是我们在运行的时候会发现这个会抛出异常之后,还是会继续执行的;

这个主要原因就是我们的interrupt唤醒这个线程之后,我们的异常抛出把这个设置的标志位清除掉了,就是我们的自动设置的标志位没有了效果,我们的异常让这个标志位的效果失效了,接下来,线程可以自己决定接下来如何进行处理;

image-20241021174301710

11.线程等待join

下面的这个程序就是我们的t线程会休眠,我们的t.join就会让这个主线程等待我们的t线程,直到我们的t线程执行结束,主线程才会结束;

image-20241021181517994

12.线程方法总结

image-20241021183414460
动设置的标志位没有了效果,我们的异常让这个标志位的效果失效了,接下来,线程可以自己决定接下来如何进行处理;

在这里插入图片描述

11.线程等待join

下面的这个程序就是我们的t线程会休眠,我们的t.join就会让这个主线程等待我们的t线程,直到我们的t线程执行结束,主线程才会结束;

在这里插入图片描述

12.线程方法总结

在这里插入图片描述

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

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

相关文章

微积分复习笔记 Calculus Volume 1 - 3.5 Derivatives of Trigonometric Functions

3.5 Derivatives of Trigonometric Functions - Calculus Volume 1 | OpenStax

西门子S7-200 SMART 多泵轮换功能库案例下载

通用描述 在现场使用多台风机水泵的场合,需要考虑对多台风机水泵进行轮换,因此如何合 理的对多台风机水泵进行轮换就成了一道难题,本文针对上述情况,专门开发了多 泵轮换的应用库,可以方便统计泵的运行时间&#xf…

Python print()输出颜色设置

标准格式 print("\033[显示方式;前景颜色;背景颜色m…\033[0m") 显示方式 前景颜色和背景颜色 print("\033[0;37;41m我是小杨我就这样\033[0m") print("\033[0;37;42m我是小杨我就这样\033[0m") print("\033[0;37;…

AI助理与知识库:企业新人培训的革新力量

在快速变化的商业环境中,企业新人培训模式的创新已成为提升组织效能的关键。特别是人工智能(AI)助理的引入,结合知识库的应用,为企业新人培训带来了革命性的变化。以下是对这一变革的深入探讨与前景展望,旨…

文本转语音工具 | Balabolka v2.15.0.880 便携版

Balabolka是一款功能强大的文本转语音(TTS)软件,它能够将文字转换成语音并保存为多种音频格式,如WAV、MP3、OGG或WMA。这款软件兼容多种文件格式,包括但不限于AZW、CHM、DjVu、DOC、EPUB、FB2、LIT、MOBI、ODT、PDF、P…

3.堆栈的理解

堆栈是同一段进行插入删除的线性表 (先入后出) 栈式最基础的常见的数据结构之一 进入一个新的函数的时候 会开辟一个空间,存放需要的数据 int add(int a,int b,int c) {return abc } int main() {add(1,2,3) }//add(1&#xff…

Redis 线程控制 总结

前言 相关系列 《Redis & 目录》(持续更新)《Redis & 线程控制 & 源码》(学习过程/多有漏误/仅作参考/不再更新)《Redis & 线程控制 & 总结》(学习总结/最新最准/持续更新)《Redis &a…

https://huggingface.co/上的模型无法用linux服务器clone怎么办(只需要稍微改一下网址,就可以切换到镜像下载)

问题描述: 在ubuntu系统上,使用如下命令,克隆仓库,报无法访问错误: git clone https://huggingface.co/distilbert/distilroberta-base通用解决方案: 把下面部分更换: https://huggingface.…

Scrapy框架原理与使用流程

一.Scrapy框架特点 框架(Framework)是一种软件设计方法,它提供了一套预先定义的组件和约定,帮助开发者快速构建应用程序。框架通常包括一组库、工具和约定,它们共同工作以简化开发过程。scrapy框架是python写的 为了爬…

为什么有0.35/Tr这一信号带宽定义

从频域幅值函数可以近似认为这是一个低通滤波器模型,可以采用RC网络模型来处理,根据电路理论计算电压10%到90%所需上升时间,再根据滤波器频域特性计算幅值在-3db处的频率极限,通过两个关系式可以计算出频率极大值和上升时间关系&a…

<<机器学习实战>>15-26节笔记:逻辑回归参数估计、梯度下降及优化、模型评价指标

梯度下降缺点:有可能有鞍点(如果不是凸函数的时候),不一定能找到最小值解决方法:随机梯度下降(选一条数据)和小批量梯度下降(选几条数据这两个解决方法又会带来新问题,比…

51单片机完全学习——LCD1602液晶显示屏

一、数据手册解读 通过看数据手册我们需要知道,这个屏幕每个引脚的定义以及如何进行发送和接收。通过下面这张图我们就可以知道,这些引脚和我们的编程是有关的,需要注意的是,这里我们在接线的时候,一定要把DB0-DB7接到…

变流升压一体机:能源领域的新兴力量与优秀品牌

在当今能源转型的大背景下,变流升压一体机作为一种创新型的电力设备,正逐渐成为新能源领域的关键角色。 变流升压一体机是一种综合性很强的光伏箱式变电站设备,它将储能变流器、升压变压器、高压环网柜、低压配电箱等多种设备集成在一个集装箱…

LeetCode.冗余连接(并查集以及广度优先搜索)

684.冗余连接| 传送门:. - 力扣(LeetCode) 树可以看成是一个连通且 无环 的 无向 图。 给定往一棵 n 个节点 (节点值 1~n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间,且这条附加的边不属于树中…

上线 24 小时,爆了!

产研团队(兼客服)已爆单🔥🚀💥!(bushi) 在此由衷感谢各位小伙伴的信任🤩! 还没有试用的小伙伴赶紧去围观👇️👇️👇️ …

高效数据集成案例:从聚水潭·奇门到MySQL

聚水潭奇门数据集成到MySQL的技术案例分享 在企业信息化建设中,数据集成是实现业务流程自动化和数据统一管理的关键环节。本文将分享一个具体的系统对接集成案例:如何将聚水潭奇门平台上的销售出库单数据高效、可靠地集成到MySQL数据库中,以…

AUTOSAR-Com模块

COM 文章目录 COMCOM 基础介绍COM主要功能AUTOSAR COM 模块 发送模型Signal 信号/信号组发送信号属性—Triggered属性Pending属性信号的初始化信号的对齐方式(大小端)信号的收发发送接收 字节序转换和符号扩展信号的过滤机制过滤处理信号传输模式信号流和…

【十进制转十六进制数】

【十进制转十六进制数】 C语言版本C 版本Java版本Python版本 💐The Begin💐点点关注,收藏不迷路💐 从键盘接收一个整数,编程实现将其转换成十六进制数。 输入 一个整数 输出 十六进制数 样例输入 100样例输出 6…

day01-ElasticStack+Kibana

ElasticStack-数据库 #官网https://www.elastic.co/cn/ #下载7.17版环境准备 主机名IP系统版本VMware版本elk110.0.0.91Ubuntu 22.04.417.5.1elk210.0.0.92Ubuntu 22.04.417.5.1elk310.0.0.93Ubuntu 22.04.417.5.1 单机部署ES 1.下载ES软件包,放到/usr/local下 […

HTML3D旋转相册

文章目录 序号目录1HTML满屏跳动的爱心(可写字)2HTML五彩缤纷的爱心3HTML满屏漂浮爱心4HTML情人节快乐