【JavaEE】多线程(二)

news2024/11/26 10:31:10

多线程(二)

文章目录

  • 多线程(二)
    • 第一个多线程程序
    • 观察线程
    • sleep
    • 创建线程
      • 继承Thread类,重写run方法
      • 实现Runnable, 重写run
      • 继承Thread,重写run
      • 实现Runnable,重写run
      • 基于lambda表达式
    • Thread的常见构造方法
      • Thread几个常见属性
        • setDaemon();
        • isAlive();

续上文, 多线程(一),我们可以了解到,多线程和普通程序的区别:

  • 每一个程序都是一个独立的执行流
  • 多个线程之间都是“并发”执行的

第一个多线程程序

class MyTread extends Thread{
    @Override
    public void run() {
        //这个方法就是线程的入口方法
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("hello thread");
        }
    }
}

//演示创建线程
public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new MyTread();
        //start 和 run 都是 Thread 的成员
        //run 只是描述了线程的入口(线程主要做什么任务)
        //start 则是真正调用了系统API,在系统中创建线程,让线程再调用run
        //t.start();
        //t.run();
        t.start();
        while (true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

通过运行这个程序,我们可以发现两个while循环在“同时执行”,可以看到打印结果是两边的日志在交替打印的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这也说明了:

每个线程都是一个独立执行的逻辑,也就是独立的执行流~

我们也可以形象的看作是:兵分两路,并发执行~

并发 => 并行 + 并发 => 并发编程的效果 => 充分使用多核cpu资源

不过当我们将main 函数的t.start();改成t.run();

public static void main(String[] args) throws InterruptedException {
        Thread t = new MyTread();
        //start 和 run 都是 Thread 的成员
        //run 只是描述了线程的入口(线程主要做什么任务)
        //start 则是真正调用了系统API,在系统中创建线程,让线程再调用run
        t.run();
        while (true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }

此时,代码中不会创建新的线程,就只有一个主线程,这个主线程只能依次执行循环,执行完一个在执行另外一个~,不过因为代码中是while(true),是不会循环结束的,所以代码也就无法走到hello main了。

还有一个需要我们了解的:main这个线程是jvm自动创建的,和其他线程比起来并没有什么特殊的,在一个java进程中,至少都会有一个main线程。


观察线程

当我们多线程程序运行的时候,我们可以使用IDEA或者jconsole来观察该进程内的多线程情况~

这里我们主要介绍jconsolejconsolejdk自带的程序

jconsole我们可以在jdk包里找到

  1. 首先我们要先找到jdk的安装地址

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2. 在地址处找到jdk然后打开bin目录,然后再列表中找到jconsole.exe

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里我们要注意的是:

  • 在启动jconsole.exe之前,我们得确保IDEA的程序已经跑起来了

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

sleep

上述的线程,我觉得他在while循环中转的太快了,想要他慢点~

那我们就可以使用Thread.sleep();,sleepThread静态方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

需要注意的是:我们使用sleep的时候汇报这样的错误

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

MyRunnable里的异常是受查异常,必须要显示处理,此处必须try catch,不能用throws,在这个代码中是重写父类的run,父类的run没有throws,子类方法也就不能也有。

而这个程序每秒所打印出来的内容,顺序都是不确定的。

因为这两线程都是休眠1000ms,当时间到后,谁先谁后是不一定的,这个过程可以视为“随机”

操作系统,对于多个线程的调度顺序,是不确定,随机的,而此处的随机不是数学上的概率均等的随机,是取决于 操作系统 对于线程调度的模块(调度器)来具体实现的~

创建线程

继承Thread类,重写run方法

package Thread.test_8_12;
class MyTread extends Thread{
    @Override
    public void run() {
        //这个方法就是线程的入口方法
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("hello thread");
        }
    }
}

//演示创建线程
public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new MyTread();
        //start 和 run 都是 Thread 的成员
        //run 只是描述了线程的入口(线程主要做什么任务)
        //start 则是真正调用了系统API,在系统中创建线程,让线程再调用run
        //t.start();
        //t.run();
        t.start();
        while (true){
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}

实现Runnable, 重写run

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

public class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new MyRunnable();
        Thread t = new Thread(runnable);
        t.start();

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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

好处

使用Runnable的写法,和直接继承Thread之间的区别就是:解耦合

使用Runnable接口重写run方法相对于直接继承Thread类的方式更加灵活、可扩展,并且能够实现解耦合。我们可以将任务逻辑与线程的实现分离,通过实现Runnable接口,我们能够更好地控制线程的行为并在需要时更好地管理和复用线程。


这里我们举个例子:

你老婆想要喝水,但是她又懒得去接水,于是她就会叫你或者你5岁孩子去接水。

而接水就是一个任务,你执行还是你孩子执行,这是没有本质区别的,那么此时我们就可以将接水这个任务单独提取为Runnable,后续是谁都可以轻轻松松完成这个任务~

但是如果任务变了呢?接水 -> 泡茶

那么此时这个任务就只能你来完成了,你那5岁孩子完成不来任务

那么这个任务就是和你这个线程有一定耦合关系的~


而我们创建一个线程,需要进行两个关键操作:

  1. 明确线程要执行的任务

    任务本身,不一定和线程 概念 强相关的

    • 这个任务只是单纯的执行一段代码,这个任务是使用单个线程执行还是多个线程执行,亦或是别的方式(信号处理函数/协程/线程池)都没什么区别~
    • 任务本身,就可以将任务本身提取出来,此时就可随时把代码改成使用其他方式来执行这个任务~
  2. 调用系统的 api 创建线程


继承Thread,重写run

package Thread.test_8_12;
public class Demo3 {
    public static void main(String[] args) {
        //匿名内部类
        Thread t = new Thread(){
            @Override
            public void run() {
                while (true){
                    System.out.println("hello world");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

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

**外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
**

实现Runnable,重写run

package Thread.test_8_12;
public class Demo4 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello world");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        Thread t = new Thread(runnable);
        t.start();

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

基于lambda表达式

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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

那为什么lambda表达式不用重写run方法?
实际上啊,lambda自身就是run方法

  • lambda:本身就是用来表示逻辑的,使用lambda就能描述出当前的线程要干嘛
  • run方法:线程的入口,通俗来说就是:告诉线程你要干啥子~

Thread的常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用Runnable对象创建线程对象,并命名
Thread(TreadGroup group, Runnable target)线程可以被用来分组管理,分好的几位线程组

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

public class Demo7 {
    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();
                }
            }
        }, "这是一个新线程");
        t.start();
    }

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Thread几个常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台进程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

setDaemon();

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一般默认下,一个进程是前台线程~

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();
                }
            }
        }, "这是一个新线程");

        //设置 t 为后台线程
        t.setDaemon(true);
        t.start();
    }

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

调用setDaemon(true)方法,将t设置为后台线程。后台线程是一种特殊的线程,当所有前台线程(例如主线程)都结束时,后台线程会自动终止

改成后台线程之后,主线程飞快执行完了,此时就没有其他前台线程了,于是线程结束,t线程来不及执行,就 over 了~~


isAlive();

Thread 对象的生命周期,要比系统内核中的线程更长一些~

Thread对象还在,内核中的线程已经销毁了这样的情况(不求同年同月同日生,也不求i同年同月同日死)

所以我们可以使用isAlive();来判定内核线程是不是已经没了,也就是回调函数执行完毕,线程就没了

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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


下面用一张图概括一下本篇文章所讲的内容~

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

至此,多线程(二)先写到这,后续会持续更新,敬请期待~

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

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

相关文章

ETL与ELT理解

ETL ETL( Extract-Transform-Load),用来描述将数据从来源端经过抽取(Extract)、转换(Transform)、加载(Load)至目的端的过程。ETL模式适用于小数据量集。如果在转换过程…

利用hutool工具类实现验证码功能

hutool工具类实现验证码 一.生成验证码二.校验验证码三.使用案例1.引入hutool工具类2.VerifyCodeResp接口响应体3.VerifyCodeController验证码工具类4.测试验证5.项目结构及源码下载 利用hutool工具类,可以很方便生成不同类型的验证码。这里简单记录下使用过程。 一…

1500*C. Tenzing and Balls (线性DP)

解析: 每次选择两个相同的数,删去他们以及他们之间的所有数,问最多可以删除多少? DP,对于某个位置 i ,其前面有多个 j 使得 a[i]a[j],所以使用 f[i] 来记录前 i 个数能够删除的最大值。 #inclu…

内网横向移动

内网横向移动 当攻击者在拿下一台内网主机后,通常会利用当前拿下的机器当作跳板,进一步攻击 内网其他主机,扩大攻击影响范围。 攻击机:Kali Linux 靶机: Windows server 2008 WEB 10.10.10.20…

C: . 与 -> 的区别

相同点&#xff1a; 功能相同&#xff1a;访问结构体或者类的成员。优先级相同。 不同点&#xff1a; 结构体变量用 . 来访问成员&#xff1b;结构体指针用 ->来访问成员&#xff1b; #include <stdio.h> #include<string.h> //首先定义结构体类型student&a…

达摩院SPACE对话大模型:预训练语言模型,预训练对话模型,知识注入

01 预训练语言模型 VS 预训练对话模型 1. 大规模语言模型 过去几年 NLP 领域的重大进展&#xff0c;主要是大型预训练模型出现与大规模使用。预训练语言模型有了很大的发展&#xff0c;出现了很多变种。但是&#xff0c;本质上都还是语言模型&#xff0c;如上图右边的流程图所…

练习-使用ApplicationContext中事件发送以及AOP来实现代码解耦

案例要求 将用户注册与用户发送消息之间进行解耦 实现 ApplicationContext接口 具体文章见&#xff1a;BeanFactory与ApplicationContext_熵240的博客-CSDN博客 创建事件类 package com.example;import org.springframework.context.ApplicationEvent;public class UserRegis…

回溯算法解决分割回文串

回溯算法解分割回文串 力扣131 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。 回文串 是正着读和反着读都一样的字符串。 示例 1&#xff1a; 输入&#xff1a;s "aab" 输出&#xff1…

婚恋相亲系统小程序源码金媒v10.0版 红娘系统pc端+h5端+公众好端统一后台管理模块

婚恋相亲系统小程序源码 金媒v10.0版本 红娘系统正版pc端+h5端+公众好端 统一后台管理 模块:vip模块,活动报名,红娘模块,上榜模块,婚恋文章,动态模块,红娘认领,认证模块(五种,有实名,手机,学历,收入,房子)等等。 多种模板 后台 管理员后台 红娘独立后台,…

Python 字符串格式化

视频版教程 Python3零基础7天入门实战视频教程 我们前面学到字符串拼接&#xff0c;如果简单的2,3个字符串拼接那还算好&#xff0c;如果大于3个变量的拼接&#xff0c;就比较麻烦。还有一点&#xff0c;就是字符串无法和和其他类型直接拼接&#xff0c;需要进行类型转换&…

地奥“畅依笙”革故鼎新,新品面市! 呵护肠胃 助力大健康!

根据平安健康发布的《2021国民肠道健康调研报告》&#xff0c;肠道问题逐渐年轻化&#xff0c;约90%的国人都会遇到肠道问题。我国肠胃终端产品持续热销&#xff0c;根据2021年淘宝天猫全年益生菌保健品/功能食品市场规模&#xff0c;销售额达63.2亿元&#xff1b;目前益生菌类…

Unity减少发布打包文件的体积——获取精灵图片的信息限制它的大小

一、起因 一个工程&#xff0c;打包成webGL且压缩成zip文件后&#xff0c;接近400M&#xff0c;后来把大的精灵图片设置最大尺寸&#xff0c;降低大小后&#xff0c;再次发布&#xff0c;zip文件缩减到250M 二、如何一键获得工程里面的精灵图片信息 三、获取精灵图片信息 1、…

智能工单系统的报修流程是怎样的?工单管理系统有什么作用?

“的修”智能工单管理系统适用于多个行业&#xff0c;包括学校、医院、酒店、企事业单位和社区物业。它基于微信小程序平台和APP开发&#xff0c;支持实现故障报修、报修状态查询以及服务评价等功能。用户可以轻松使用微信进行报修&#xff0c;以及查询报修的进度、状态和对服务…

【Linux从入门到精通】多线程 | 线程互斥(互斥锁)

上篇文章我们对线程 | 线程介绍&线程控制介绍后&#xff0c;本篇文章将会对多线程中的线程互斥与互斥锁的概念进行详解。同时结合实际例子解释了可重入与不被重入函数、临界资源与临界区和原子性的概念。希望本篇文章会对你有所帮助。 文章目录 引入 一、重入与临界 1、1 可…

网络安全深入学习第一课——热门框架漏洞(RCE-命令执行)

文章目录 一、RCE二、命令执行/注入-概述三、命令执行-常见函数四、PHP命令执行-常见函数1、exec&#xff1a;2、system3、passthru4、shell_exec5、反引号 backquote 五、PHP命令执行-常见函数总结六、命令执行漏洞成因七、命令执行漏洞利用条件八、命令执行漏洞分类1、代码层…

目标检测评估指标mAP:从Precision,Recall,到AP50-95【未完待续】

1. TP, FP, FN, TN True Positive 满足以下三个条件被看做是TP 1. 置信度大于阈值&#xff08;类别有阈值&#xff0c;IoU判断这个bouding box是否合适也有阈值&#xff09; 2. 预测类型与标签类型相匹配&#xff08;类别预测对了&#xff09; 3. 预测的Bouding Box和Ground …

C高级day5(Makefile)

一、Xmind整理&#xff1a; 二、上课笔记整理&#xff1a; 1.#----->把带参宏的参数替换成字符串 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX(a,b) a>b?a:b #define STR(n) #n int main(int argc, const char *argv…

sheetjs實現頁面的數據導出execl

概述 需要給頁面的table做一個數據導出功能,發現一個好用sheetjs工具 只需要簡單的js語法如下,就可以將table的數據導出來 function load(){var date new Date();date.setTime(date.getTime() (8 * 60 * 60 * 1000));var table document.getElementById("tab");v…

神领物流 day02-网关与支付 Spring Cloud Alibaba 微服务

课程安排 单token存在的问题双token三验证用户端token校验与鉴权对接三方支付平台分布式锁 1、场景说明 新入职的你加入了开发一组&#xff0c;也接到了开发任务&#xff0c;并且你也顺利的修复了bug&#xff0c;完成了快递员、司机的鉴权&#xff0c;现在的你已经对项目的业…

C++qt day8

1.用代码实现简单的图形化界面&#xff08;并将工程文件注释&#xff09; 头文件 #ifndef MYWIDGET_H #define MYWIDGET_H //防止头文件冲突#include <QWidget> //父类的头文件class MyWidget : public QWidget //自定义自己的界面类&#xff0c;公共继承…