Thread的基本用法

news2025/2/25 3:55:01

目录

1.创建线程

2.1 继承Thread类,重写run方法

2.2 实现Runnable,重写run方法

2.3 使用匿名内部类继承Thread类,重写run方法

2.4 使用匿名内部类实现Runnable接口,重写run方法

2.5 使用lambda表达式

3.Thread类的构造方法

4.Thread类的常见属性的获取方法

5.线程方法的使用

5.1 前台线程与后台线程

5.2 线程的执行顺序

5.3 线程休眠

5.4 线程中断

5.5 线程等待

5.6 获取线程实例


1.创建线程

在我们没有使用多线程编程之前,我们代码的运行全部都由main线程(main线程也是主线程)独自运行,而使用多线程的编程之后,我们的代码可以在多个线程上"同步"运行,下面先介绍创建线程的5种方法

2.1 继承Thread类,重写run方法

class MyThread extends Thread{
    @Override
    public void run() {
        while(true) {
            System.out.println("hello Thread");
        }
    }
}

public class ThreadDemo1 {

    public static void main(String[] args){
        Thread t = new MyThread();

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

首先创建一个类,由这个类去继承Thread类,重写其中的run方法

run方法: 创建出来的线程去执行的内容

start方法:  创建出一个新的线程,并让其去执行run方法里面的内容

执行上面的代码,会出现hello Thread 和 hello main交替打印的情况

他们的打印杂乱无章,可能是t线程(创建出来的线程)先打印,也可能是main线程先打印

这是由于: 当执行完t.start()之后,创建出来一个新的线程(简称t线程),t线程和main线程同属于在同一个进程上,他们在操作系统内核中的调度情况可能是并行,也可能是并发,是无法预知的,所以打印的前后顺序也是无法预知的.

sleep方法: sleep方法是Thread类中的一个静态方法,它可以让线程进入短暂休息的状态,比如sleep(1000) 就是休息1秒.(注意:sleep方法需要处理异常)

有了sleep方法,就可以更直观的去观察线程的输出情况,如下:

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

public class ThreadDemo1 {

    public static void main(String[] args){
        //创建一个Thread对象,new MyThread()
        Thread t = new MyThread();

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

2.2 实现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();
    }
}

首先创建一个类,实现Runnable接口,重写里面的run方法.

将Runnable对象传到Thread的构造方法中,然后t.start()线程就创建好了.

2.3 使用匿名内部类继承Thread类,重写run方法

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

和2.1 类似,只不过使用了匿名内部类.

2.4 使用匿名内部类实现Runnable接口,重写run方法

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

和 2.2 类似,将类实现替换成内部类实现

2.5 使用lambda表达式

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

仿照代码中去写就好了,大括号里面的内容就相当于重写run方法的内容

3.Thread类的构造方法

方法1:Thread()

创建线程对象

方法2:Thread(Runnable r)

使用Runnable对象创建线程对象

方法3:Thread(String name)

创建线程对象,并初始化线程的名字

方法4:Thread(Runnable r,String name)

使用Runnalbe对象创建线程,并初始化线程的名字

4.Thread类的常见属性的获取方法

(1) getId()

获取线程的ID

(2) getName()

获取线程的名字

(3) getState()

获取线程的状态

(4) getPriority()

获取线程的优先级

(5) isDaemon()

判断线程是否为后台线程(true/false)

(6) isAlive()

判断线程是否存活(true/false)

(7) isInterrupted()

判断线程是否被中断(true/false)

5.线程方法的使用

5.1 前台线程与后台线程

前台线程: 会阻止进程结束,如果前台线程没有执行完,进程是无法结束的.

后台进程: 不会阻止进程结束,即使后台线程没有执行完,进程也是可以结束的.

我们在main方法中创建的线程会默认为前台线程,但是我们可以使用setDaemon()方法去设置线程

如下面的代码:

当执行完t.setDaemon(true) 之后,t线程就被设置成了后台线程,此时当主线程(main线程)执行完之后,t线程也就结束了.

因为当main线程执行晚最后一条语句时,它的任务就完成了,main线程会自动销毁(t线程如果执行完run方法的内容后也会自动销毁),而此时这个进程中只有main一个前台线程,当main线程被销毁后,其他的后台线程也就结束了.

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 Thread");
                }
            }
        }, "MyThread");
        t.setDaemon(true);
        t.start();
    }
}

5.2 线程的执行顺序

线程的执行顺序是不确定的,如下面这个代码:

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

        t.start();

        for(int i = 0;i < 3;i++) {
            try {
                Thread.sleep(1000);
                System.out.println("hello main");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

当执行完 t.start() 之后,t线程被创建,此时t线程执行run方法中的内容,同时main线程继续执行main方法中下面的内容,此时,main线程遇到sleep(),休眠1s,所以会先打印 "hello Thread" ,而之后会先打印谁,后打印谁是不确定的.

因为在微观上,操作系统内核在调度两个线程时是使用并行还是并发是不确定的,也就导致了打印的顺序是不确定的.

 

5.3 线程休眠

线程的休眠和前面提到的sleep方法有关

当线程处于sleep状态时,就是处于休眠状态

如下:

public class Test {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while(true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}

当前这个t线程就是处于休眠状态.

5.4 线程中断

线程中断相当于线程终止(结束),这个操作完全取决于设计线程里面的代码,我们给出的中断信息,只是传递了一个让其中断的信息,至于是否会真的被中断、被什么时候中断都是不确定的.

而传递中断信息可以有两种方法:

第一种:使用自己设置的标志位去传递中断信息

public class ThreadDemo8 {
    private static boolean flag = true;
    public static void main(String[] args) throws InterruptedException {
        Thread t= new Thread(() -> {

            while(flag) {
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t.start();

        Thread.sleep(3000);
        flag = false;
    }
}

我们设置了一个boolean类型的静态成员变量 flag,让它成为我们的标志位,当我们将flag设置为false时,程序就会停止,至于会迅速停止的原因也是因为对run方法的设计.

第二种:使用Thread类自带的标志位

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();
                    //等待0.5s在进行终止
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException ex) {
                        e.printStackTrace();
                    }
                    break;
                }
            }
        });

        t.start();
        Thread.sleep(3000);
        t.interrupt();
    }
}

Thread类中有一个方法是Thread.currentThread().isIntercurrpted(),它的返回值默认为false

当外部调用t.interrupt()时 它的返回值会传变为true.

但是如果调用t.interript()方法时,t进程处于休眠状态(sleep状态),会立即别唤醒,被唤醒之后sleep会报出异常,但是代码仍然会继续执行.

因为sleep会进行清除标志位的操作,也就是将修改为true的标志位,修改会false

那么,为什么sleep要进行清除标志位的操作呢?

因为当程序被唤醒后,它不清楚程序是否要进行中断操作,将这个问题抛给了程序员,此时线程是否中断,完全取决于程序员设计的代码.

比如上面的事例代码,我在里面加入了break操作,让循环终止,程序结束,那么线程也就随之销毁,是执行了中断操作;而我又在break前面加上了一个sleep,让其先休眠 0.5s 再进行中断操作,这也就对应上了我最后开始说的:线程是否被中断,取决于其中代码的实现.

5.5 线程等待

线程等待使用的操作是Thread中的join()方法,它用类的实例来调用.

当执行t.join()方法后,当前执行的线程就会进入等待(也成为线程阻塞),等到t线程执行完,才可以继续执行当前的线程.

如下代码:

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

        t.start();

        //如果在join之前t线程也就结束了,join就不会再阻塞,会立即返回
        //Thread.sleep(5000);


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

        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("join 之后");
    }
}

上面的代码的执行顺序是如下这样:

join 之前

...(t线程的三遍 hello Thread 打印)

join 之后

也就是main线程等待t线程,当t线程执行完毕,join()才让main线程停止等待

而如果在执行到 t.join() 之前join线程就执行完了,那么main线程就不会等待.

比如:如果将上面代码注释的sleep(5000)打开,那么执行的结果就会变成下面的情况:

...(t线程的三遍 hello Thread 打印)

join 之前

join 之后

5.6 获取线程实例

Thread.currentThread()方法

它是一个类方法,作用是获取当前线程的实例

也就是说: 你在哪个线程中去调用这个方法,那么它就会返回这个线程的实例对象.

public class ThreadDemo11 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println(Thread.currentThread().getName());
        },"t线程");
        t.start();
    }
}

此时上面的胆码输出为 -- t线程

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

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

相关文章

游戏开发28课 cocoscreator 渲染排序说明

渲染排序说明 2D 渲染节点排序 2D 渲染节点可分为在 Canvas 下的节点和不在 Canvas 下的节点两种&#xff1a; 在 Canvas 下的节点可参考下文 UI 节点排序 部分的内容。 不在 Canvas 下的节点&#xff0c;用户可选择通过 自定义材质 来开启深度检测实现和 3D 物体的遮挡显示…

面试中经常被问到的几个回溯问题

前言&#xff1a;大家好&#xff0c;我是小威&#xff0c;24届毕业生&#xff0c;本篇文章是对最近复刷的回溯相关的算法题进行记录&#xff0c;方便复习使用。 如果文章有什么需要改进的地方还请大佬不吝赐教&#x1f44f;&#x1f44f;。 小威在此先感谢各位大佬啦~~&#x1…

马尔可夫预测案例分析

根据已经有数据进行预测的研究方法有很多&#xff0c;包括arima模型、指数平滑法、灰色预测等&#xff0c;本文针对马尔可夫预测进行阐述。比如研究中国移动&#xff0c;中国联通和中国电信三家运营商&#xff0c;他们的用户可以互相携号转网&#xff0c;已经当前3家运营商的市…

开发者友好的Linux常见命令整理

系统本身 uname -a # 查看内核/操作系统/CPU信息 cat /etc/redhat-release 查看服务器版本 cat /proc/cpuinfo # 查看CPU信息 hostname # 查看计算机名 lspci -tv # 列出所有PCI设备 lsusb -tv # 列出所有USB设备 lsmod # 列出加载的内核模块 env # 查看环境变量 查看…

领域驱动设计-领域建模

领域建模 领域建模是针对问题空间的战术求解的过程&#xff1a;观察真实世界的业务需求&#xff0c;对业务知识进行提炼和转换&#xff0c;排除技术因素对建模产生的影响&#xff0c;一切围绕着业务需求而来。同时满足未来的需求变更与产品维护 快速建模法&#xff1a;名次动…

通信原理技术复习重点知识

1.感知控制层通信的目的和特点&#xff1a; 目的&#xff1a;将各种传感设备&#xff08;数据采集设备以及相关的控制设备&#xff09;所感知的信息在较短的通信距离内传送到信息汇聚系统&#xff0c;并由该系统传送到网络传输层 特点&#xff1a;传输距离近&#xff0c;传输方…

使用 SAP WebIDE 将 SAP UI5 应用部署到 ABAP 系统时遇到的关于传输请求的错误

有朋友遇到一个在 webide 里部署 SAP UI5 应用到 ABAP Repository 里时出错的问题&#xff1a; 错误消息&#xff1a; Failed to get transports for the selected package. Request POST /webidedispatcher/destinations/LND500/sap/bc/adt/cts/transportchecks failed: Forbi…

【数学】仿射变换(续1)

在 这篇文章 中我介绍了椭圆中的圆幂定理。其中&#xff0c;椭圆中的“相交弦定理”为 PA⋅PBPC⋅PDrAB2rCD2\frac{PA\cdot PB}{PC \cdot PD}\frac{r_{AB}^2}{r_{CD}^2}PC⋅PDPA⋅PB​rCD2​rAB2​​ 本来以为它是从来见不到的东西 然而 请看此题&#xff1a; 以下给出两种常…

传奇登录器打不开的四种原因

最近很多传奇玩家或者GM都遇到了传奇登陆器打不开&#xff0c;没反应&#xff0c;提示无法访问指定设备等问题&#xff0c;导致很多游戏没有办法玩&#xff0c;让玩家心情沮丧&#xff0c;作为GM&#xff0c;那么就更伤心了&#xff0c;很多玩家进不来游戏&#xff0c;开服数千…

Vue3框架中路由的使用和局部刷新的功能(第十一课)

使用vue-router的步骤:p第一步&#xff1a;创建路由需要映射的组件&#xff08;打算显示的页面&#xff09;&#xff1b;p第二步&#xff1a;通过createRouter创建路由对象&#xff0c;并且传入routes和history模式&#xff1b;配置路由映射: 组件和路径映射关系的routes数组&a…

linux杀毒软件clamav安装

clamav 简介 ClamAV is an open source (GPLv2) anti-virus toolkit, designed especially for e-mail scanning on mail gateways. It provides a number of utilities including a flexible and scalable multi-threaded daemon, a command line scanner and advanced tool …

Git的基本使用

Git 1.Git基础知识 1.1Git简介 更新的历史保存在Gitee中不会丢失的。 1.简介&#xff1a;Git是一个分布式版本管理系统&#xff0c;是为了更好地管理Linux内核开发而创立的。 Git可以在任何时间点&#xff0c;把文档的状态作为更新记录保存起来。因此可以把编辑过的文档复…

AI强势入场,成就史上最快足球

众所周知&#xff0c;卡塔尔是全球最富裕的国度之一&#xff0c;是世界第一大液化天然气生成和出口国。丰富的石油资源&#xff0c;几乎让每一名原住民从出生之日起就实现财务自由&#xff0c;人均GDP高达6万多美元&#xff0c;钞能力毋庸置疑。 2022年世界杯正是在这片富饶的土…

图论 - 拓扑排序

有向图的拓扑序列 给定一个 nnn 个点 mmm 条边的有向图&#xff0c;点的编号是 111 到 nnn&#xff0c;图中可能存在重边和自环。 请输出任意一个该有向图的拓扑序列&#xff0c;如果拓扑序列不存在&#xff0c;则输出 −1。 若一个由图中所有点构成的序列 AAA 满足&#xf…

调试-一些奇怪的现象-以及const的作用

今天给大家看一些奇怪的例子&#xff0c;我们用调试来看看这到底是什么情况&#xff0c;本次选取环境为vs2019 话不多说&#xff0c;我们直接来看例子 #include <stdio.h>int main() {int i 0;int arr[10] { 0 };for (i 0; i < 12; i){arr[i] 0;printf("he…

压缩包加密、解密

压缩包文件大家都经常使用&#xff0c;经常用来压缩文件之后再转发给别人&#xff0c;网上下载的软件&#xff0c;有时候也是压缩包形式下载下来&#xff0c;解压之后才是软件。相信大家一定也遇到过加密的压缩包文件。今天和大家分享如何加密压缩包&#xff0c;以及如何解密压…

114.(leaflet篇)leaflet空间判断-点与圆的空间关系

听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <!DOCTYPE html> <html>

【SpringBoot】对于yaml的详细学习和三种属性赋值的实战详解

一.yaml详细讲解 1.1 什么是yaml&#xff1f; YAML是一种数据序列化语言&#xff0c;通常用于编写配置文件。业界对YAML有不同的看法。有些人会说YAML代表另一种标记语言。其他人认为“YAML不是标记语言”&#xff08;“YAML并非标记语言”&#xff09;。“YAML”只是这句话的…

CentOS中使用Docker部署带postgis的postgresql

场景 CentOS中使用Docker来部署Postgresql&#xff1a; CentOS中使用Docker来部署Postgresql_霸道流氓气质的博客-CSDN博客 上面安装的postgresql还需自行安装postgis插件&#xff0c;是否可以直接安装带postgis插件的postgresql。 注&#xff1a; 博客&#xff1a;https:…

RKMEDIA使用简介

瑞芯微&#xff08;rockchip&#xff09;芯片现在嵌入式行业使用的逐渐多了&#xff0c;本栏主要介绍rv1126/rv1109上的媒体框架 rkmedia的使用以及常遇到的问题。 希望可以给各位使用rkmedia的小伙伴一些帮助&#xff0c;同时也是自己工作的记录。 rkmedia的手册在sdk目录下/…