JavaEE之多线程编程:2.创建线程及Thread类常见方法(超全!!!)

一、创建线程

Java中创建线程的写法有很多种!!!这里介绍其中5种。

方法1:继承Thread类,重写run

创建一个类,让这个类继承自Thread父类,再重写我们的run方法就可以了。
使用Thread类,不需要import别的包,因为它是再Java.lang下面的。

//写一个类,继承自标准库的Thread
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("hello word!");
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        //创建线程,是希望线程成为一个独立的执行流(执行一段代码)
        //创建线程是相当于雇了个人帮我们干活
        Thread t = new MyThread(); //这里就不用new标准库的thread的了,而是刚才创建的子类
        t.start(); //线程中的特殊方法,启动一个线程
    }
}

注意:
start() 是创建了一个新的线程,由新的线程来执行t.run()方法。

这个新的线程就是调用操作系统的API
通过操作系统内核创建新线程的PCB,并且把要执行的指令交给这个PCB,当PCB被调度到了CPU上执行的时候,也就执行到了线程run方法的代码了。

如果只是在main方法中输出"hello world",你的Java进程主要就是有一个线程(调用main方法的线程),主线程通过t.start(),主线程调用stat(),创建出一个新的线程,新的线程调用t.run(),如果我们run()方法执行完毕,这个线程自然销毁了。

方法2:实现Runnable接口

//Runnable 作用是描述一个“要执行的任务”,run 方法就是任务的执行细节。
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("hello thread");
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        //这只是描述了个任务
        Runnable runnable = new MyRunnable();
        //把任务交给线程来执行
        Thread t = new Thread(runnable);
        t.start();

    }
}

解耦合。目的就是为了让 线程和任务(线程要干的活)之间分离开。
未来如果要改代码不用多线程,使用多进程、线程池或协程……此时代码改动比较小

方法3:使用匿名内部类,继承Thread

//使用匿名内部类来创建线程
public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread t = new Thread() {
            @Override
            public void run() {
                super.run();
                System.out.println("hello");
            }
        };
        t.start();
    }
}

其中new Thread()
①创建了一个Thread子类,(子类没有名字)所以才叫做“匿名”;
②创建了子类的实例,并且让 t 引用指向该实例。

方法4:使用匿名内部类,实现Runable

public class ThreadDemo4 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        });
        t.start();
    }
}

这个写法和方法2本质相同,只不过把实现Runable任务交给匿名内部类的语法。
此处是创建了一个类,实现Runable,同时创建了类的实例,并且传给Thread的构造方法。

方法5:使用Lambda表达式

此方法是最简单,最推荐的方法。

public class ThreadDemo5 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("helloDemo5");
        });
        t.start();
    }
}

把任务用lambda表达式来描述,直接把lambda传给Thread构造方法。
lambda就是个匿名函数(没有名字的函数),用一次就没有了。
() - > {}

二、Thread类及常见方法

Thread类是JVM用来管理线程的一个类,换句话说,每个线程都有一个Thread对象与之相关联
可以说,每个执行流,都需要有一个对象来描述,类似下图,而Thread类的对象,就是用来描述一个线程执行流的,JVM会将这些Thread对象组织起来,用于线程调度,线程管理。
在这里插入图片描述

1. Thread的常见构造方法

在这里插入图片描述
其中:
最后两个Thread(String name)Thread(Runnable target, String name) 多了个name参数,这个参数是为了方便调试而给线程起了个名字。
线程的默认的名字为 thread-0,1,2之类的。

构造方法的格式如下:

Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread(“这是我的名字”);
Thread t4 = new Thread(new MyRunnable(), “这是我的名字”);

例如:

public class ThreadDemo6 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello");
                }
            }
        },"mythread");
        t.start();
    }
}

2. Thread的几个常见属性

在这里插入图片描述

  • ID是线程的唯一标识,不同线程不会重复;

  • 名称是各调试工具用到的;

  • getStat:构造方法里起的名字;

  • getStat:线程状态,Java中线程的状态要比操作系统原生的状态更丰富一些;

  • getPrior:线程的优先级可以获取,也可以设置,但是设置了不管用,因为优先级是很多要素影响得到的;

  • isDaem:是否守护线程,是否“后台线程”
    前台线程会阻止进程结束,前台线程的工作没做完,进程是完不了的;
    后台线程,不会阻止线程结束,后台线程没做完,进程是可以结束的。
    代码里手动创建的线程,默认都是前台线程,包括main,默认也是前台;
    其他JVM自带的线程都是后台线程。
    也可以手动使用setDaemon设置成后台线程,是后台线程,就是守护线程。
    在这里插入图片描述
    把t设置成 守护/后台线程,此时进程的结束与否就和t无关了。

  • isAlive()是否存活:判断当前系统中的这个线程是不是有了。
    在用户态(应用程序)中创建了一个线程Thread t = new Thread();
    程序中通过t.start();来调用,
    在真正调用start之前,调用t.isAlive(),此时是false,此时内核态(操作系统内核)里没有这个线程;
    而调用start后,就会让内核创建一个PCB,此时这个PCB才表示一个真正的线程,此时isAlive是 true。
    也可以简单的理解为start/run方法是否允许结束了。

    另外,如果内核里线程把run执行完了,此时线程销毁,PCB随之释放。但是Thread t 这个对象还不一定被释放,此时isAlive也是false。
    【总结】
    如果 t.run还没执行,isAlive 为 false
    如果 t,run 正在执行,isAlive 为 true
    如果 t.run执行结束,isAlive 为 false

    通过上述我们可以了解,Thread t这个对象比内核里的PCB存在的周期要久。

3. 启动一个线程——start()

之前我们已经看到了如何通过复写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。

  1. 复写run方法是提供给线程要做的事情的指令清单;
  2. 线程对象可以认为是把李四、王五等新线程叫过来了;
  3. 而调用start()方法,就是相当于喊一声“行动起来!”,线程才真正独立去执行了。

总之,run方法是描述了我们要做啥任务,而stat才是真正开始任务。
调用start方法,才真正在操作系统的底层创建出一个线程!

4. 终止一个线程

终止线程的意思是,不是让线程立即就停止,而是通知线程,你应该要停止了,但是是否真的停止,取决于线程这里的具体写法。终止线程有以下两种方式:

  1. 使用标志位来控制线程是否要停止;
public class ThreadDemo8 {
    private static boolean flag = true;

    public static void main(String[] args) throws InterruptedException{
        Thread t = new Thread(() -> {
            while (flag) { //当flag为true的时候
                System.out.println("hello!!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
            t.start();

            Thread.sleep(3000); //当执行到第三秒的时候,把flag变为false,结束循环
            flag = false;
        }
}

在这里插入图片描述
这个代码之所以能够起到作用,修改flag,t线程就结束,完全取决于t线程内部的代码,代码里通过flag控制循环。
因此,这里只是告诉让这个线程结束,这个线程是否要结束,什么时候结束,都是线程内部自己代码来决定的。因为是while(flag)所以3秒钟后结束了,要是while(true),怎么改都不会结束循环。

此方法的缺点:自定义变量这种方式,不能及时相应,尤其在sleep休眠的时间比较久的时候。

  1. 使用Thread自带的标志位( interrupt() 方法),来进行判定是否要停止。

这里调用interrupt,只是通知终止,不是线程一定要乖乖终止!!
【格式】

while (!Thread.currentThread().isInterrupted()) {}

其中:
① while判定,判断条件是true,则执行方法体内的循环,为false,则循环结束;
Thread.currentThread() :这是Thread类的静态方法,通过这个方法可以获取到当前线程。哪个线程调用这个方法,就是得到哪个线程的对象引用(类似于this);
isInterrupted() 为true表示被终止,为false表示未被终止,继续执行;
④ 前面有个 ! 逻辑取反符,所以当isInterrupted() 为true时,!isInterrupted() 就为false,反之,则为true。

interrupt 会做两件事:
① 把线程内部的标志位(boolean)给设置成 true;
② 如果线程在进行sleep,就会触发异常,把sleep给唤醒。

但是sleep在唤醒的时候,还会做一件事,就是把刚在设置的这个标志位,再设置回false。(清空了标志位),这就导致,当sleep的异常被catch完了之后,循环继续执行!
如下述例子:

【例1】:线程t忽视了终止请求

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

        t.interrupt(); //3秒后,把线程内部的标志位(boolean)给设置成 true
    }
}

在这里插入图片描述
此时循环会一直执行下去!这里就是sleep清空的例子。

【例2】:线程t立即响应了终止请求(加了break)

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

        t.interrupt(); //3秒后,把线程内部的标志位(boolean)给设置成 true
    }
}

在这里插入图片描述

【例3】:稍后进行终止
执行完等了3秒钟,代码执行完毕。

public class ThreadDemo9 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("hello thread!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //稍后再终止!
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                }
                    break;
                }
            }
        });
        t.start();
        Thread.sleep(3000);

        t.interrupt();
    }
}

【总结】
大前提:调用interrupt,只是告诉线程你该终止了,但是它是不是真的终止,这是它自己的事情。
注意其中sleep有个清楚标志位的情况,唤醒之后,线程终不终止,立即还是稍后终止,就把选择全交给程序员自己了。

5. 等待一个线程

有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作,这是我们需要一个方法明确等待线程的结束
可理解为等待一个线程结束!
线程是一个随机调度的过程,等待线程做的事,就是再控制两个线程结束的顺序。

【例1】

public class ThreadDemo10 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                System.out.println("hello!!!");
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t.start();
        System.out.println("join 之前");

        //此处的join就是让当前的main线程来等待t线程执行结束(等待t的run执行完)
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("join之后");
    }
}

在这里插入图片描述
执行过程:
t 调用了start()后,t 线程和 main主线程就并发执行,分头行动;
主线程这里打印了“join之前”,同时t线程打印了hello thread;
我们的t线程在执行过程中,主线程并没有打印join之后,而是在join这里等待了一会(发生阻塞block),等待3s之后,t线程执行完了,我们的主线程才会执行后面的 “join之后”;
主线程,等待t线程彻底执行完毕之后,才继续往下执行了。
通过输出,我们也能看出 t 线程肯定比 main 线程先结束。

【例2】

public class ThreadDemo10 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                System.out.println("hello!!!");
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("join 之前");

        //此处的join就是让当前的main线程来等待t线程执行结束(等待t的run执行完)
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("join之后");
    }
}

在这里插入图片描述
在join前加了一个等待5s钟,此时在执行join的时候,t 已经结束了,所以join不会堵塞,就会立即返回。
在这里插入图片描述

  • public void join() :无参版本,一直等;
  • public void join(long millis) :指定一个超时时间(最大等待时间),这种是最常见的,一直等很容易又问题。

【总结】
线程没结束,就等待,线程结束了,就立即返回;
总之可以保证这两个线程的返回顺序。

6. 获取当前线程引用

public static Thread currentThread();
返回当前线程对象的引用。

调用这个方法,不需要实例,直接通过类名来调用

	Thread t = new Thread();
	Thread.currentThread();

在哪个线程中调用,就能获取到哪个线程的实例

7. 休眠当前线程

是我们比较熟悉的一组方法,有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的。

public static void sleep(long millis) throws InterruptedException 休眠当前线程 millis
毫秒
public static void sleep(long millis, int nanos) throws InterruptedException 可以更高精度的休眠

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis());
        Thread.sleep(3 * 1000);
        System.out.println(System.currentTimeMillis());
   }
}

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

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

相关文章

[已解决]diffusers加载的模型都被放在哪里了

1 模型位置 /root/.cache/huggingface/hub ~/.cache/huggingface/hub 2 unet位置 值得注意的是需要进入这个目录才可以看到unet文件 /root/.cache/huggingface/hub/models--nota-ai--bk-sdm-base-2m/snapshots/e8b5597155c5b2c77585570b99113f1c77b97338

java方法引用语法规则以及简单案例

目录 一、方法引用1.1 什么是方法引用1.2 方法引用的语法规则1.3 构造器引用1.4 方法引用的简单案例 参考资料 一、方法引用 1.1 什么是方法引用 方法引用是 Lambda 表达式的一种简写形式&#xff0c;用于表示已有方法的直接引用。 类似于lambda表达式&#xff0c;方法引用也…

echarts自定义tooltip位置和内容

tooltip: {trigger: item,backgroundColor: none,position: function (pos, params, dom, rect, size) {//我这个是每次显示30条数据 所以这么判断var obj params.dataIndex < 15 ? "right" : "left"return obj;},formatter: (params) > {//收入和…

Spring Boot--Freemarker渲染技术+实际案例

目录 Freemarker 1.1.什么是Freemarker 1.2.Freemarker模板组成部分 1.3.优点 FreeMarker常见的方法&#xff1a; 2.2.2.数值 2.2.3.布尔值 2.2.4.日期 2.3.常见指令 2.3.1.处理不存在的值 assign 2.3.4.list 2.3.5.include SpringBoot整合Freemarker Freemarker…

MYsql第三次作业

目录 问题&#xff1a; 解答 1.查询student表的所有记录 2.查询student表的第2条到4条记录 3.从student表查询所有学生的学号&#xff08;id&#xff09;、姓名&#xff08;name&#xff09;和院系&#xff08;department&#xff09;的信息 4.从student表中查询计算机系和…

Qt设置类似于qq登录页面(ikun)

头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QWindow> #include <QIcon> #include <QLabel> #include <QMovie> #include <QLineEdit> #include <QPushButton>QT_BEGIN_NAMESPACE namespace Ui { class…

分类预测 | Matlab实现HPO-GRU【23年新算法】基于猎食者优化算法优化门控循环单元的数据分类预测

分类预测 | Matlab实现DBO-SVM蜣螂算法优化支持向量机的数据分类预测【23年新算法】 目录 分类预测 | Matlab实现DBO-SVM蜣螂算法优化支持向量机的数据分类预测【23年新算法】分类效果基本描述程序设计参考资料 分类效果 基本描述 1.HPO-GRU【23年新算法】基于猎食者优化算法优…

算法导论复习(二)

算法导论第二次复习以 分治法 为专题 文章目录 分治算法是什么归并排序Strassen矩阵乘法最近点对 求解递推表达式 分治算法是什么 归并排序 代码如下&#xff1a; #include <iostream> #include <vector>using namespace std;// 归并函数&#xff0c;将两个有序数…

vue router-view报错解决

目录 1 报错内容2 解决方案2.1 检查版本2.1 正确引入使用vue-router组件 1 报错内容 我们在使用router-view组件的时候会遇到如下报错&#xff1a; 报错内容如下&#xff1a; [Vue warn]: Unknown custom element: <router-view> - did you register the component co…

企业首选的免费开源供应链管理协作系统功能应用介绍

本文节选自Odoo亚太金牌服务机构【开源智造】所编写的《Odoo最佳业务解决方案》如需获取完整的知识内容&#xff0c;请至开源智造官网免费获取。感谢网友一键三连&#xff1a;点赞、转发、收藏&#xff0c;您的支持是我们最大的前进动力&#xff01; 供应链协作 用Odoo供应链协…

redis-学习笔记(Jedis hash简单命令)

hset & hget 往 hash 里面塞数据和获取数据 示例代码 hmset & hmget 批量插入数据, 获取数据 注意, hmset 里面插入的是一个 Map hmget 的返回值是一个一个 List 列表 (参数仍是变长参数) 示例代码 hexists 判断 hash 中 域值 存不存在 示例代码 hdel 删除指定的域和值…

5G/4G工业DTU扬尘在线监测:解决工地扬尘困扰的最佳方案

在如今快速发展的工业环境中&#xff0c;扬尘污染成为了一个严重的问题。工地扬尘不仅对环境造成污染&#xff0c;还对工作人员的健康产生负面影响。为了解决这一问题&#xff0c;5G/4G工业DTU扬尘在线监测应运而生。 5G/4G工业DTU扬尘在线监测原理 5G/4G工业DTU扬尘在线监测是…

SpringBoot之视图渲染技术

前言 在Spring Boot中&#xff0c;视图渲染技术用于将动态数据渲染到用户界面&#xff0c;生成最终的HTML、XML、JSON等文档&#xff0c;以便将其返回给客户端浏览器 一.关于Freemarker 1.介绍 Freemarker是一个Java模板引擎&#xff0c;用于生成基于模板的动态内容。它是一…

【深度学习】Pytorch 系列教程(一):PyTorch数据结构:1、Tensor(张量)及其维度(Dimensions)、数据类型(Data Types)

文章目录 一、前言二、实验环境三、PyTorch数据结构0、分类1、Tensor&#xff08;张量&#xff09;1. 维度&#xff08;Dimensions&#xff09;0维&#xff08;标量&#xff09;1维&#xff08;向量&#xff09;2维&#xff08;矩阵&#xff09;3维张量 2. 数据类型&#xff08…

用AI画个女朋友回家过年,1行Python代码,免费实现

#这才是真功夫# 大家好&#xff0c;这里是程序员晚枫&#xff0c;全网同名。 马上过年了&#xff0c;还是单身的举个爪&#xff01; 今年GPT系列的产品非常火爆&#xff0c;今天给大家分享一下&#xff0c;如何免费用AI代码画1个女朋友。&#x1f447; 直接上代码 大家学习 或 …

STM32-TIM定时器输出比较

目录 一、输出比较简介 二、PWM简介 三、输出比较通道&#xff08;通用&#xff09; 四、输出比较通道&#xff08;高级&#xff09; 五、输出比较模式 六、PWM基本结构 七、PWM参数计算 八、外设介绍 8.1 舵机 8.2 直流电机及驱动 九、开发步骤 十、输出比较库函数…

容器技术与操作系统

文章目录 容器技术 vs 虚拟机操作系统容器 Docker与操作系统 容器技术 vs 虚拟机 操作系统 操作系统是一个很重而且很笨的程序&#xff0c;简称笨重&#xff0c;有多笨重呢&#xff1f; 操作系统运行起来是需要占用很多资源的&#xff0c;大家对此肯定深有体会&#xff0c;刚…

vue.js单页面 如何遇到404页面如何正确返回状态码404

客户端配置&#xff08;Vue.js&#xff09; 在客户端&#xff0c;你可以在 Vue 路由器&#xff08;vue-router&#xff09;中设置一个捕获所有未定义路由的规则&#xff0c;显示一个 404 组件&#xff0c;但请注意这不会改变 HTTP 状态码。 import Vue from vue; import Rout…

【源码复现】《Towards Deeper Graph Neural Networks》

目录 1、论文简介2、论文核心介绍2.1、基本概述2.2、模型介绍 3、源码复现3.1、torch复现3.2、DGL复现 1、论文简介 论文题目——《Towards Deeper Graph Neural Networks》论文作者——Meng Liu, Hongyang Gao & Shuiwang Ji论文地址——Towards Deeper Graph Neural Net…

uni-app 一些实用的页面模板

时间倒计时 <!-- 时间倒计时 --> <template><view class"container"><view class"flex-row time-box"><view class"time-item">{{ laveTimeList[0] }}</view><text>天</text><view class&qu…