Thread类的基本用法(详解版)

news2024/9/22 13:35:37

什么是线程?

线程是操作系统能够进行调度的最小单位,通常被视为轻量级的进程。线程在同一进程中共享进程的资源(如内存,打开的文件,网络等),但每一个线程都有自己的执行栈、程序计数器和局部变量。

为什么要引进线程?

        首先,“并发编程”成为“刚需”。

  • 单核CPU的发展遇到了瓶颈,想要提高计算能力,就需要多核CPU,而并发编程能更充分利用多核资源。
  • 有些任务场景需要“等待io”,为了让等待IO的时间能够去做一些其他工作,也需要用到并发编程。
    其次,虽然多进程也能实现并发编程,但是线程比进程更轻量。
  • 创建线程比创建进程更快
  • 销毁线程比销毁进程更快
  • 调度线程比调度进程更快  

1.线程的创建

        a.继承Thread类
//继承Thread来创建一个线程类:
class MyThread extends Thread{
    @Override
    //重写run方法
    public void run() {
        System.out.println("hello Thread");
    }
}

public class Demo1 {
    public static void main(String[] args) {
        //创建MyThread类的实例
        MyThread thread = new MyThread();
        //调用start()方法,启动线程
        thread.start();
    }
}
        b.实现Runnable接口
//实现Runnable接口
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(" hello Thread2");
    }
}

public class Demo2 {
    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        //创建 Thread 类实例, 调⽤ Thread 的构造⽅法时将 Runnable 对象作为 target 参数.
        Thread t = new Thread(runnable);
        //调用start方法
        t.start();
    }
}
c.匿名内部类创建 Thread 子类对象
// 使⽤匿名类创建 Thread ⼦类对象
public class Demo3 {
    public static void main(String[] args) {
        Thread t = new Thread(new Thread(){
            
            @Override
            public void run() {
                System.out.println("hello Thread3");
            }
        });

        t.start();
    }
}
d.匿名内部类实现Runnable子类对象
// 使⽤匿名类创建 Runnable ⼦类对象
public class Demo4 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(" hello Thread4");
            }
        });
        
        t.start();
    }
}
 e.lambda 表达式创建 Runnable 子类对象
// 使⽤ lambda 表达式创建 Runnable ⼦类对象
public class Demo5 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println(" hello Thread5");
        });
        
        t.start();
    }
}

2.Thread的常见构造方法

Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("this is my name");
Thread t4 = new Thread(new MyRunnable(), "this is my name");

3. Thread 的几个常见属性

  • ID是线程的唯一标识,不同的线程不会重复
  • 名称是各种调试工具用到的
  • 状态表示线程当前所处的一个情况
  • 优先级高的理论上来说更容易被调度到
  • JVM会在一个进程的所有非后台进程结束后,才会结束运行
  • 简单理解为run方法是否执行完
  • 线程中断问题,下面会进一步说到
public class Demo6 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t1.start();
        System.out.println("ID:"+Thread.currentThread().getId());
        System.out.println("线程名称:"+Thread.currentThread().getName());
        System.out.println("线程状态:" +Thread.currentThread().getState());
        System.out.println("线程是否存活:"+Thread.currentThread().isAlive());
        System.out.println("线程的优先级:"+Thread.currentThread().getPriority());
        System.out.println("线程是否被中断:"+Thread.currentThread().isInterrupted());
        System.out.println("是否为后台线程"+Thread.currentThread().isDaemon());


    }
}

4.线程的中断

假设B线程正在运行,A线程想要让B结束。其实核心就是A要想办法,让B的run方法执行完毕,此时B就自然结束了,而不是说,B的run执行一半,直接把B强制结束。

java中,结束线程,是一个“温柔”的过程,不是直接简单粗暴的。主要是怕,B某个工作,执行一半时,被强制结束了,此时B对应的结果数据是一个“半成品”(我们更希望,是一个完整的过程)。

a.通过共享的标记来进行沟通
public class Demo7 {
    private static boolean flg = false;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while(!flg) {
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println("t 线程执行结束");
        });

        t.start();

        Thread.sleep(3000);
        //修改flg变量,就能够影响到t线程结束了
        System.out.println("main线程尝试终止t线程");
        flg = true;
    }
}
b. 调用 interrupt() 方法来通知
public class Demo8 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            //获取当前引用 Thread类的静态方法 就能获取到调用这个方法的线程的实例
            Thread t2 = Thread.currentThread();
            //Thread类里面有一个成员,就是interrupted boolean类型的 初始情况下,这个变量是false,未被终止的
            //一旦外面的其他线程调用一个interrupted方法,就会设置上述标志位
            while(!t2.isInterrupted()) {
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(" t 线程结束");
        });

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

那么根据上述的理解,此代码是能够跑起来的。接下来我们来看看是否正确呢?

 抛出了一个RuntimeException异常,由于判定isInterrupted()和执行打印这两个操作太快了。因此整个循环,主要的时间都花在sleep上面了。main在调用interrupted的时候,大概率t线程处于sleep状态中呢。此处的interrupted不仅仅能设置标志位,还能把那个sleep操作给唤醒。调用interrupted之后,sleep被唤醒,此时就会进入catch语句,抛出异常。由于catch语句中默认再次抛出异常,没人catch,最终就交给了jvm这一层了,进程就直接异常终止了!那么有了上述原因,我们直接不抛出异常,换成一个打印。

public class Demo8 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            //获取当前引用 Thread类的静态方法 就能获取到调用这个方法的线程的实例
            Thread t2 = Thread.currentThread();
            //Thread类里面有一个成员,就是interrupted boolean类型的 初始情况下,这个变量是false,未被终止的
            //一旦外面的其他线程调用一个interrupted方法,就会设置上述标志位
            while(!t2.isInterrupted()) {
                System.out.println("hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    //throw new RuntimeException(e);
                    System.out.println("执行到catch语句了");
                }
            }
            System.out.println(" t 线程结束");
        });

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

我们接着运行看看效果:

 可以发现,一直在打印hello Thread,说明还是有问题的,看起来上面的标志位好像没有被设置一样。

首先,interrupted肯定会设置这个标志位的,其次,当sleep等阻塞的函数被唤醒之后,就会先清空刚才设置的interrupted标志位,所以循环才会一直执行。

所以,如果想要结束循环,可以在catch中使用break或者return。

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

2.sleep被唤醒之后,又能够清除标志位

5.线程等待

操作系统中,针对多个线程的执行,是一个“随机调度,抢占式执行”的过程。

线程等待,就是确定两个线程的“结束顺序”。我们虽然无法确定两个线程调度执行的顺序,但是可以控制,谁先结束,谁后结束。让后结束的线程,等待先结束的线程即可。此时后结束的线程就会进入阻塞,一直到先结束的线程真的结束了,阻塞才会解除。

例如:现在有两个线程A,B    

在A线程中调用B.join,意思就是让A线程等待B线程先结束,然后A再继续执行!

public class Demo9 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {

            for (int i = 0; i < 3; i++) {
                System.out.println("hello Thread1");
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                //让t2线程等待t1线程先完成它的任务
                t1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 3; i++) {
                System.out.println("hello Thread2");
            }
        });

        t2.start();
        t1.start();
    }
}

5.线程休眠

两个参数的方法可以进一步精确控制睡眠的时长。 

public class Demo10 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try {
                //让线程休眠3秒的时间
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t线程休眠完");
        });

        t.start();
        //主线程等待t线程先完成任务
        t.join();
        System.out.println("main线程等待完成t线程");
    }
}

那么我们运行程序在显示台上就会看到,三秒后才会开始打印"t线程休眠完",因为我们是在main线程中写的t.join(),所以我们的main线程会等待t线程先完成任务后,main线程才开始执行自己的任务。

好了,本期的分享就到这里了,我们下期再见咯! 

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

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

相关文章

项目管理中什么是项目质量管理?

所有项目经理都易于犯下的通病便是产生无谓的乐观&#xff0c;误以为质量会自然而然地融入项目之中。然而&#xff0c;在实际操作中&#xff0c;我们必须明确界定、监控并管理项目管理质量。 项目质量管理的核心在于确保项目质量。这被称为项目的第四个约束&#xff0c;经理们必…

澄志创投马良骏:营销技术行业下一个关键词是合并与整合 | 中国广告营销行业资本报告深访①

日前&#xff0c;澄志创投发布了《中国广告营销行业资本报告2022》&#xff0c;投中信息提供独家数据支持&#xff0c;Marteker联合撰写。报告围绕广告营销行业的资本运作等话题&#xff0c;采访了行业资深人士。 马良骏Joshua&#xff08;澄志创投创始人&#xff09; 问&#…

Module not found: Can‘t resolve ‘tls‘/Module not found: Can‘t resolve ‘net‘

最近使用nextjs开发了一套系统&#xff0c;包含了前后端&#xff0c;但是前后端的界限其实没有那么清晰&#xff0c;很多地方都引入了公共的内容&#xff0c;甚至互相引入了其模块的内容 然后周五时在前端引入了一个方法&#xff08;该方法在前端的模块里面定义的&#xff0c;之…

【Linux】基本命令(第二篇)

目录 1.命令提示符 2.命令的基本格式 1) 选项的作用 2) 参数的作用 2.cd命令&#xff1a;切换目录 3.pwd命令&#xff1a;显示当前路径 4.ls命令&#xff1a;查看目录下文件 5.mkdir命令&#xff1a;创建目录&#xff08;文件夹&#xff09; 6.rmdir命令&#xff1a;删…

《黑神话:悟空》横空出世:全新国产3A里程碑之作

“直面天命”、“棒打虎先锋”、“猿神&#xff0c;启动”......在过去的一周里&#xff0c;从朋友圈到微博热搜&#xff0c;从咖啡联名到陕西文旅&#xff0c;几乎已经没有人&#xff0c;可以对《黑神话&#xff1a;悟空》这一国产3A里程碑之作视而不见。8月20日上午10时&…

eclipse汉化教程

1:选择HELP->install New software 2:点击Add 3&#xff1a;name随便填写&#xff0c;location&#xff1a;https://download.eclipse.org/technology/babel/update-site/latest/ 4&#xff1a;等待下载完成后点next 5&#xff1a;选择中文&#xff0c;再点击next 6. 等待…

【实战】Spring Boot 嵌套事务REQUIRES_NEW与NESTED在项目中的运用

文章目录 引言1. 什么是Nested Transactions?2. Spring Boot中的事务管理2.1 基本用法2.2 Nested Transactions的需求场景 3. 实现Nested Transactions3.1 使用Propagation.REQUIRED)/Propagation.NESTED)3.2 嵌套事务REQUIRES_NEW与NESTED3.3 注意事项 4. 测试Nested Transac…

upload-labs(Pass-18 ~ Pass-21)

1、Pass-18(条件竞争) 1、题目需要进行代码审计&#xff1a; <?php include ../config.php; include ../head.php; include ../menu.php;$is_upload false; $msg null;if(isset($_POST[submit])){$ext_arr array(jpg,png,gif);//白名单$file_name $_FILES[upload_fil…

OpenLayers3,地图探查功能实现

文章目录 一、前言二、代码实现三、总结 一、前言 图层探查&#xff0c;即对置于地图下方的图层进行一定范围的探查&#xff0c;以便用户查看到不易察觉的地理地况。本文基于OpenLayers3&#xff0c;实现地图探查的功能。 二、代码实现 <!DOCTYPE HTML PUBLIC "-//W…

滴滴拼车系统开发前景分析

滴滴拼车系统作为滴滴出行平台的一个重要组成部分&#xff0c;其开发前景分析如下&#xff1a; 市场需求&#xff1a;随着城市交通压力的增加和共享经济的普及&#xff0c;拼车服务因其便捷性和经济性受到越来越多用户的青睐&#xff0c;市场需求持续增长 。政策支持&#xf…

商业律师事务所借助 DocuSign 解决方案加快了 QES 和身份识别流程 | 电子签约律师事务解决方案

Roosbeh Karimi 是一位充满活力的年轻律师&#xff0c;他创办的商业律师事务所正引领着法律行业的数字化转型 KARIMI.legal 是一家总部位于柏林的商业律师事务所&#xff0c;专门从事商法、竞争法和法律技术集体诉讼。该商业律师事务所拥有一支由 11 名员工组成的团队&#xff…

音视频不同步问题总结

音视频同步的定义 指在视频播放过程中&#xff0c;图像和声音的播放时间保持一致&#xff0c;使得观众感觉到图像与声音是同时发生的。在实际的音视频 处理过程中&#xff0c;由于音频和视频的处理速度可能不同&#xff0c;或者由于网络传输的延迟&#xff0c;可能会导致音视…

地瓜直播间开播啦 | RDK X5-RWKV大模型部署实战

地瓜机器人新一代机器人开发者套件RDK X5&#xff0c;搭载旭日5智能计算方案&#xff0c;极简机器人开发体验&#xff0c;助力机器人开发一步通关&#xff08;更多产品信息请关注2024地瓜机器人开发者日暨新品发布活动&#xff0c;后续将在本公众号发布&#xff09;。 RWKV&…

零基础入门转录组数据分析——预后模型之lasso模型

零基础入门转录组数据分析——预后模型之lasso模型 目录 零基础入门转录组数据分析——预后模型之lasso模型1. 预后模型和lasso模型基础知识2. lasso预后模型&#xff08;Rstudio&#xff09;——代码实操2. 1 数据处理2. 2 构建lasso预后模型2. 3 提取Lasso预后基因2. 4 计算风…

Java框架myBatis(三)

一、特殊符号转义 特殊符号处理 在mybatis中的xml文件中&#xff0c;存在一些特殊的符号&#xff0c;比如&#xff1a;、"、&、<> 等&#xff0c;正常书写mybatis会报错&#xff0c;需要对这些符号进行转义。 具体转义如下所示&#xff1a; 特殊字符 转义字符…

图解 Elasticsearch 的 Fielddata Cache 使用与优化

1、难搞的 fielddata cache 在 ES 使用的几个内存缓存中&#xff0c;fielddata cache 算是一个让人头疼的家伙。 作为和 query cache 和 request cache 一样不受 GC 控制的内存使用者&#xff0c;fielddata cache 虽然也有 indices.fielddata.cache.size 的设置来阻止过度使用&…

vite-plugin-ejs:打包时报错:hook is not a function

现象&#xff1a;打包时提示hook is not a function 解决方法1&#xff1a; 在node_modules中找到vite-plugin-ejs的index.js&#xff0c;将handler修改为transform&#xff1a; 解决方法2&#xff1a; 使用vite --version命令查看本机的vite版本&#xff0c;根据插件的写法选…

WMS仓储管理系统的这些功能模块一定要做好

在当今物流行业迅猛发展的背景下&#xff0c;仓储管理的智能化升级已成为企业提升竞争力的关键一环。智能立体仓库系统的构建&#xff0c;正是这一趋势下的重要里程碑&#xff0c;它以高度自动化、精准化的货物处理能力&#xff0c;重新定义了仓储作业的标准。而这一切的核心驱…

CAD波浪线画法2

cad波浪线怎么画出来 - 软件自学网下面给大家介绍的是cad波浪线怎么画出来的方法&#xff0c;具体操作步骤如下&#xff1a;https://rjzxw.com/jiaocheng/18774.html这个是对的&#xff0c;适合多个版本

网络安全系统性学习路线「全文字详细介绍」

&#x1f91f; 基于入门网络安全打造的&#xff1a;&#x1f449;黑客&网络安全入门&进阶学习资源包 一、基础与准备 网络安全行业与法规 想要从事网络安全行业&#xff0c;必然要先对行业建立一个整体的认知&#xff0c;了解网络安全对于国家和社会的作用&#xff0…