【JavaEE初阶】多线程(2)

欢迎关注个人主页:逸狼


创造不易,可以点点赞吗~

如有错误,欢迎指出~



目录

线程的核心操作

创建线程start

线程的状态

线程的终止

定义一个变量实现

利用标志位实现

加上break/return 结束线程 

线程等待

join 无参数版本

两个线程等待

多个线程等待

缺陷

join有参数版本


线程的核心操作

创建线程start

一个经典的面试题:start和run之间的区别

  • start:调用系统函数,真正在系统内核中创建线程(创建PCB,加入到链表中),此处的start 会根据不同的系统,分别调用不同的api(windows,linux,mac...),创建好新的线程,再单独来执行run.
  • run:描述了线程要执行的任务,也可以称为"线程的入口"

start的执行速度一般是比较快的(创建线程,比较轻量),一旦start执行完毕,新线程就会开始执行,调用start的线程,也会执行main. 调用start,不一定非得是main线程调用,任何线程都可以创建其他线程,如果系统资源充裕,就可以任意的创建线程(但线程不是越多越好)

start的本质调用系统的api,系统的api会在系统内核里 ,创建线程(创建PCB,加入链表)

线程的状态

由于Java希望一个Thread对象 只能对应到一个系统中的线程,因此会在start中 根据线程的状态做出判定:

  • 如果Thread对象 没有调用过start,此时状态是一个NEW状态,接下来就可以顺利调用start
  • 如果已经调用了start,就会进入到其他状态,只要不是NEW状态,接下来start就会抛出异常

 所以再次强调: 一个Thread对象,只能调用一次start,如果多次调用就会出问题(一个Thread对象,只能对应系统中的一个线程)

线程的终止

Java中让一个线程结束是一个更'温柔'的过程,比如B正在运行着,A想让B结束,其实核心是A想办法(如何将B的run能够更快的执行完)让B的run方法执行完毕,此时B自然结束了, 而不是B的run执行一半,A直接把B强制结束.

定义一个变量实现

public class Demo6 {
    private static boolean isQuit = false;

    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while(! isQuit){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("t线程结束.");
        });

        t.start();
        Thread.sleep(1000);

        System.out.println("main线程尝试终止 t线程");
//修改isQuit变量,就能够影响到t线程结束
        isQuit=true;
    }
}

其中判断条件isQuit要写在main方法的外面 作为成员变量,内部类(lambda表达式本质上是一个'函数接口'产生的 匿名内部类)是可以访问外部类的成员

知识复习: 变量捕获

变量捕获是lambda表达式/匿名内部类 的一个语法规则

isQuit 和 lambda定义在一个作用域中,此时的lambda内部是可以访问到lambda外部 的,Java把同作用域中的所有变量都给捕获了,但是Java的变量捕获要求捕获的变量得是final/ 事实final(这是Java的特殊限制,c++,js等语言没有这个限制)

利用标志位实现

Interrupt方法能够设置标志位, 也能唤醒sleep等阻塞方法.

sleep被唤醒之后,又能清空标志位

public class Demo7 {
    public static void main(String[] args) throws InterruptedException {
        Thread t =new Thread(()->{
            Thread currentThread =Thread.currentThread();
            while(!currentThread.isInterrupted()){
                System.out.println("thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t.start();
        Thread.sleep(3000);
        //在主线程中 控制t线程被终止,设置上述标志位
        t.interrupt();
    }
}

Thread类里面有一个成员变量 是interrupted (Boolean类型),初始情况下这个变量是false(未被终止),但是一旦外面的其他线程调用一个interrupt方法,就会设置上述标志位

修改代码,停止报错

加上break/return 结束线程 

 所以,Java中 终止线程是一个'温柔'的过程,不是强行终止 ,举例: 如果A希望B线程终止,B收到这样的请求后,B需要自行决定 是否要终止/立即 /稍后 终止(B线程内部的代码自行决定,其他线程无权干涉)

  • 如果B线程想无视A,就直接catch中,啥都不做,B线程仍然会继续执行(sleep清除标志位,就可以使B能够做出这样的选择,否则B势必会结束)
  • 如果B线程想立即结束,就直接在catch中加上return或者break.此时B线程会立即结束
  • 如果B线程想稍后结束,就可以在catch中加上一些其他的逻辑(比如 释放资源,清理一些数据,提交一些结果...收尾工作)  这些逻辑之后,再进行return或者break.

线程等待

操作系统 针对多个线程的执行,是一个 '随机调度,抢占式执行'过程, 线程等待 就是在确定两个线程的'结束顺序'(无法确定两个线程调度执行的顺序,但可以控制 谁先结束,谁后结束), 本质上是让后结束的线程 等待先结束的线程即可(此时后结束的线程就会进入阻塞,一直到先结束的线程真的结束了,阻塞才解除)

join 无参数版本

对于join来说,无参数版本会持续等('死等'). 被等待的线程,只要不执行完,这里的等待就会持续阻塞

两个线程等待

使用join 比如,有两个线程a和b,在a中调用b.join,表示让a线程等待b线程先结束,然后a再继续执行(b是被等待的一方,先结束)

public class Demo8 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            for (int i = 0; i < 3; i++) {
                System.out.println("这是线程t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("t线程结束");
        });
        t.start();
        
        //让主线程 等待t线程
        System.out.println("main线程开始等待");
        t.join();
        System.out.println("main线程等待结束");
    }
}

如果上面的 t线程先结束了,main线程就不必等待了(join就不会发生阻塞),join 就是确保被等待的线程能够结束,如果已经结束了,join就不必在等了.

任何的线程之间都是可以相互等待的

下面是 t线程等待 主线程的代码示例

public class Demo10 {
    public static void main(String[] args) throws InterruptedException {
        //t线程等待主线程
        Thread mainThread =Thread.currentThread();//拿到主线程对象

        Thread t =new Thread(()->{
            System.out.println("t线程开始等待...");
            try {
                mainThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t线程等待结束");
        });
        t.start();
        Thread.sleep(2000);
        System.out.println("main线程执行结束.");

    }
}

 

获取主线程对象 和 sleep的理解:

多个线程等待

相互等待的线程 也不一定是两个线程之间,一个线程也可以同时等待多个别的线程,或者若干个线程之间也能相互等待

public class Demo9 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 3; i++) {
                System.out.println("线程t1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("t1执行结束");
        }) ;

        Thread t2 =new Thread(()->{
            for (int i = 0; i < 4; i++) {
                System.out.println("线程t2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            try {
                t1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2执行结束");
        });
        t1.start();
        t2.start();
        System.out.println("main线程开始等待...");
        t1.join();
        t2.join();
        System.out.println("main线程结束等待.");
    }
}

上述代码中,主线程等待t1和t2线程,t2线程等待t1,所以结束顺序是t1, t2, main线程

t1, t2和主线程这三个日志的顺序是不确定的(不确定哪个先执行),但是由于t1和t2有创建开销,一般来说是主线程先执行,t1和t2 的顺序不确定.

缺陷

对上述 join无参数版本会出现'死等'的状态   一旦被等待线程代码 出现了一些bug,就可能使这个线程迟迟无法结束,从而使等待的线程一直无法执行其他操作.

join有参数版本

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

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

相关文章

如何从 Bak 文件中恢复 SQL数据库?(3种方法)

如何从 .bak 文件恢复 SQL数据库&#xff1f; 在数据库管理和维护过程中&#xff0c;数据的安全性和完整性至关重要。备份文件&#xff08;.bak 文件&#xff09;是 SQL Server 中常用的数据库备份格式&#xff0c;它包含了数据库的完整副本&#xff0c;用于在数据丢失、系统故…

iLogtail 开源两周年:社区使用调查报告

作者&#xff1a;玄飏 iLogtail 作为阿里云开源的可观测数据采集器&#xff0c;以其高效、灵活和可扩展的特性&#xff0c;在可观测采集、处理与分析领域受到了广泛的关注与应用。在 iLogtail 两周年之际&#xff0c;我们对 iLogtail 开源社区进行了一次使用调研&#xff0c;旨…

Java入门:07.Java中的面向对象02

5 对象的使用 对象有两种使用情况 情况一&#xff1a; 对象本身&#xff0c;是一个引用类型的数据 所以他也可以像其他的所有数据一样&#xff0c;进行操作 如&#xff1a;存储&#xff0c;打印等。 int i 10 ; print(10) ; ​ //最终存储在变量中的是对象的地址。 Perso…

歌者PPT新功能速递!

本期功能更新&#xff0c;主要围绕 PPT 大纲编辑器和 PPT 翻译功能&#xff0c;全面提升了制作效率和灵活性&#xff0c;帮助你更轻松地完成 PPT 制作&#xff01;一起来看看吧&#xff5e;&#x1f447; # 功能更新 1 PPT 大纲编辑器全面更新 &#x1f4dd; 现在&#xff0c…

解决Metasploit调用Nessus报错问题

问题描述 Error while running command nessus_scan_new: undefined method []’ for nil:NilClass 解决方法 发现报错&#xff0c;经过网上查询解决方法 在Nessus服务器执行&#xff0c;下面的版本号可能有所不同&#xff0c;根据自己的情况更改&#xff0c;需要管理员身份执…

FreeRTOS学习笔记(二)任务基础篇

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、 任务的基本内容1.1 任务的基本特点1.2 任务的状态1.3 任务控制块——任务的“身份证” 二、 任务的实现2.1 定义任务函数2.2 创建任务2.3 启动任务调度器2…

HOT 100(六)二分查找、栈

一、二分查找 1、搜索插入位置 初始化左右边界&#xff1a;left 指向数组的起始位置&#xff0c;right 指向数组的末尾。二分查找过程&#xff1a;不断计算中间位置 mid&#xff0c;根据 nums[mid] 与目标值 target 的比较结果&#xff0c;调整 left 和 right&#xff0c;从而…

Lenze伦茨E82ZBC, E82ZBB E82ZMBRB安装说明手测

Lenze伦茨E82ZBC, E82ZBB E82ZMBRB安装说明手测

VMware17 虚拟机下载以及 CentOS8 操作系统安装配置 一条龙全教程

目录 一、安装 vmware workstation 虚拟机 二、安装 CentOS8 操作系统 三、安装 FinalShell 远程连接 一、安装 vmware workstation 虚拟机 安装中...&#xff08;耐心等待&#xff09; 到此安装完成&#xff0c;点击启动运行 激活码如下&#xff1a; MC60H-DWHD5-H80U…

【个人笔记】VCS工具与命令

Title&#xff1a;VCS工具学习 一 介绍 是什么&#xff1f; VCS (Verilog Compiler Simulator) 是synopsys的verilog 仿真软件&#xff0c;竞品有Mentor公司的Modelsim、Cadence公司的NC-Verilog、Verilog—XL. VCS能够 分析、编译 HDL的design code&#xff0c;同时内置了 仿…

API 网关 OpenID Connect 实战:单点登录(SSO)如此简单

作者&#xff1a;戴靖泽&#xff0c;阿里云 API 网关研发&#xff0c;Higress 开源社区 Member 前言 随着企业的发展&#xff0c;所使用的系统数量逐渐增多&#xff0c;用户在使用不同系统时需要频繁登录&#xff0c;导致用户体验较差。单点登录&#xff08;Single Sign-On&a…

Python和MATLAB(Java)及Arduino和Raspberry Pi(树莓派)点扩展函数导图

&#x1f3af;要点 反卷积显微镜图像算法微珠图像获取显微镜分辨率基于像素、小形状、高斯混合等全视野建模基于探测器像素经验建模荧光成像算法模型傅里叶方法计算矢量点扩展函数模型天文空间成像重建二维高斯拟合天体图像伽马射线能量和视场中心偏移角标量矢量模型盲解卷积和…

每日OJ_牛客_求和(递归深搜)

目录 牛客_求和&#xff08;递归深搜&#xff09; 解析代码 牛客_求和&#xff08;递归深搜&#xff09; 求和_好未来笔试题_牛客网 解析代码 递归中每次累加一个新的数&#xff0c;如果累加和大于等于目标&#xff0c;结束递归。此时如果累加和正好等于目标&#xff0c;则打…

Quartz.Net_快速开始

简述 Quartz中主要分为三部分&#xff0c;JobDetail、Trigger、Scheduler&#xff0c;分别是任务、触发器、调度器&#xff0c;三者的关系为&#xff1a;Trigger控制JobDetail的执行时间和频率&#xff0c;而Scheduler负责将具体的Trigger与具体的JobDetail绑定 1.安装Quartz…

【无线通信发展史⑧】测量地球质量?重力加速度g的测量?如何推导单摆周期公式?地球半径R是怎么测量出来的?

前言&#xff1a;用这几个问答形式来解读下我这个系列的来龙去脉。如果大家觉得本篇文章不水的话希望帮忙点赞收藏加关注&#xff0c;你们的鼓舞是我继续更新的动力。 我为什么会写这个系列呢&#xff1f; 首先肯定是因为我本身就是一名从业通信者&#xff0c;想着更加了解自…

十大口碑最好开放式蓝牙耳机是哪些?五款热销好用产品测评!

​开放式耳机现在超火&#xff0c;成了时尚、好看又舒服的代名词&#xff0c;迅速俘获了一大波粉丝&#xff0c;成了耳机界的新宠儿。跟那些传统的入耳式耳机比起来&#xff0c;开放式耳机戴着更稳&#xff0c;对耳朵也更友好。不过&#xff0c;也有人觉得这玩意儿不值&#xf…

vue3集成sql语句编辑器

使用的是codemirror 安装 pnpm add codemirror vue-codemirror --savepnpm add codemirror/lang-sqlpnpm add codemirror/theme-one-dark使用 <template><codemirror v-model"configSql" placeholder"Code goes here..." ref"codemirrorR…

AIGC与数据分析融合,引领商业智能新变革(TOP企业实践)

AIGC与数据分析融合&#xff0c;引领商业智能新变革&#xff08;TOP企业实践&#xff09; 前言AIGC与数据分析融合 前言 在当今数字化时代&#xff0c;数据已成为企业发展的核心资产&#xff0c;而如何从海量数据中挖掘出有价值的信息&#xff0c;成为了企业面临的重要挑战。随…

云服务器内网穿透连接云手机配置ALAS

文章目录 服务器安装TailscaleNAT网络&#xff08;无独立IP&#xff09;云服务器安装Tailscale有固定IP的云服务器安装Tailscale 云手机安装Tailscale开启无线网络调试安装Tailscale ALAS连接云手机 上次写到服务器连接云手机时只说了有独立IP的&#xff0c;但有独立IP的云手机…

算法打卡——田忌赛马问题

问题简介&#xff1a;就是一个贪心的思想&#xff0c;下面上题目 要求示例输出输入 大体上先比较快马&#xff0c;田的快马与王的快马 其次比较田的慢马与王的慢马&#xff0c; 两处边界比较完全之后可以直接贪心了 几份示例的代码 代码一 #include <bits/stdc.h> …