java线程中的常见方法(详解)

news2024/11/16 15:26:04

 方法简介

方法名

功能

说明

start()

启动一个新线程,在新的线程运行 run 方法中的代码

start 方法只是让线程进入就绪,里面代码不一定立刻运行(CPU 的时间片还没分给它)。每个线程对象的start方法只能调用一次,如果调用了多次会出现IllegalThreadStateException

run()

新线程启动后会调用的方法

如果在构造 Thread 对象时传递了 Runnable 参数,则线程启动后会调用 Runnable 中的 run 方法,否则默认不执行任何操作。但可以创建 Thread 的子类对象,来覆盖默认行为

join()

等待线程运行结束

join(long n)

等待线程运行结束,最多等待 n 毫秒

getId()

获取线程长整型的 id

id 唯一

getName()

获取线程名

setName(String)

修改线程名

getPriority()

获取线程优先级

setPriority(int)

修改线程优先级

java中规定线程优先级是1~10 的整数,较大的优先级能提高该线程被 CPU 调度的机率

getState()

获取线程状态

Java 中线程状态是用 6 个 enum 表示,分别为:NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED

isInterrupted()

判断是否被打断,

不会清除 打断标记

isAlive()

线程是否存活(还没有运行完毕)

interrupt()

打断线程

如果被打断线程正在 sleep,wait,join 会导致被打断的线程抛出 InterruptedException,并清除 打断标记 ;如果打断的正在运行的线程,则会设置 打断标记 ;park 的线程被打断,也会设置 打断标记

interrupted()

static,判断当前线程是否被打断

会清除 打断标记

currentThread()

static,获取当前正在执行的线程

sleep(long n)

static,让当前执行的线程休眠n毫秒,休眠时让出 cpu 的时间片给其它线程

yield()

static,提示线程调度器让出当前线程对CPU的使用

主要是为了测试和调试

 start 和 run

结论

如果在主线程中 直接调用run方法,这样其实并没有启动新线程,还是在用main线程来执行,并不能达到异步这样的效果。

使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码。

因此,启动一个线程必须用 start方法,再用新的线程去调用run方法。

示例:直接调用 run

public static void main(String[] args) {
    Thread t1 = new Thread("t1") {
        @Override
        public void run() {
            log.debug(Thread.currentThread().getName());
            FileReader.read(Constants.MP4_FULL_PATH);
        }
    };
    t1.run();
    log.debug("do other things ...");
}

输出结果

19:39:14 [main] c.TestStart - main

19:39:14 [main] c.FileReader - read [1.mp4] start ...

19:39:18 [main] c.FileReader - read [1.mp4] end ... cost: 4227 ms

19:39:18 [main] c.TestStart - do other things ...

可以看到,没有产生新线程,都是主线程,方法调用还是同步的,必须等 FileReader.read()方法执行完才能往下做“do other things ...” 

调用 start

将上面的 t1.run() 改成 t1.start()

输出

19:41:30 [main] c.TestStart - do other things ...

19:41:30 [t1] c.TestStart - t1

19:41:30 [t1] c.FileReader - read [1.mp4] start ...

19:41:35 [t1] c.FileReader - read [1.mp4] end ... cost: 4542 ms

 可以看到,产生了新线程t1,方法的调用是异步的,不用等FileReader.read()方法执行完,也能往下做“do other things ...”

sleep 与 yield

sleep

静态方法:

Thread.Sleep(1000);
//静态方法,让当前正在执行的线程进入休眠(暂时停止执行)指定的毫秒数。

作用:

调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)

注意:

在哪个线程里,就让那个线程睡眠,main方法中的thread.sleep()并不是使我们的子线程进入休眠,而是使我们的主线程进入休眠,因为sleep()方法是使当前线程进入休眠

睡眠结束后的线程未必会立刻得到执行

建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性,下面代码都是睡眠1秒,但是TimeUnit可读性更高,里面可以选时间的单位,而sleep就是用毫秒。

public static void main(String[] args) {
    Thread.sleep(1000);
    TimeUnit.SECONDS.sleep(1);
}

打断睡眠:

其它线程可以使用 interrupt 方法打断正在睡眠的线程,将其叫醒,这时 sleep 方法会抛出 InterruptedException

 

yield

英文意思是:让出、让步

调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程

具体的实现依赖于操作系统的任务调度器,如果没有其他线程,CPU空闲时,那么任务调度器会让其执行,也就是你别谦让了,没有其他人了,你来吧。

异同

都是让线程先不占用cpu

sleep是进入到 阻塞状态,yield是进入到 就绪状态,就绪状态的线程是可以被任务调度器调用执行的,但是阻塞状态的不可以。

sleep有时间参数,用于睡眠多少毫秒。yield没有时间参数

sleep应用:提高CPU效率

在没有利用cpu来计算时,不要让while(true)空转浪费cpu,这时可以使用yield或sleep来让出 cpu的使用权给其他程序

例如下面的代码,通常有些服务器端会有while (true)这样的循环来一直执行,接收请求并响应,但是如果一直while (true),那么CPU会一直执行它,导致大部分时间都是空转,所以加一个睡眠,不用睡眠太久,就可以让CPU的执行效率提高很多。

while (true){
    try {
        Thread.sleep(50);
    }catch (InterruptedException e){
        e.printStackTrace();
    }
}

join

作用

t1.join();

等待t1线程运行结束

为什么需要 join?

下面的代码执行,打印 r 是什么?

static int r = 0;
public static void main(String[] args) throws InterruptedException {
    test1();
}
private static void test1() throws InterruptedException {
    log.debug("开始");
    //t1 线程修改 静态变量r的值
    Thread t1 = new Thread(() -> {
        log.debug("开始");
        sleep(1);
        log.debug("结束");
        r = 10;
    });
    //t1 线程启动
    t1.start();
    //主线程进行打印
    log.debug("结果为:{}", r);
    log.debug("结束");
}

分析

因为主线程和线程 t1 是并行执行的,t1 线程需要 1 秒之后才能算出 r=10

而主线程一开始就要打印 r 的结果,所以只能打印出 r=0

解决方法

如何让主线程打印出最新的,也就是经过t1线程修改完毕后的r的值呢?

让主线程也进行睡眠?让主线程睡眠的时间久一点?这样显然不好,因为不好控制睡眠时间,而且t1线程睡眠完毕后不一定会被任务调度器马上调用,所以情况很复杂。

这时候就可以用 join解决这个问题:只需要在 t1.start(); 后面加上一个 t1.join(); 即可,这样主线程会等待t1线程执行完毕再执行。

有时效的 join

t1.join(1500);

interrupt

作用

可以打断正在运行的线程,也可以打断正在阻塞的线程。

  • 如果打断正在阻塞中的线程,那么会让线程进入阻塞状态
  • 如果打断正在运行的线程,那么就会打断之,但实际上不是强行打断,而是告诉其他线程:我要打断你,由其他线程决定自己是否要结束。

打断正在阻塞中的线程

打断 sleep,wait,join 的线程

这几个方法都会让线程进入阻塞状态

打断 sleep 的线程, 会清空打断状态,也就是打断状态变成false

以 sleep 为例

private static void test1() throws InterruptedException {
    Thread t1 = new Thread(()->{
        sleep(1);
    }, "t1");
    t1.start();
    sleep(0.5);//主线程先小等一会,让t1线程进入睡眠
    t1.interrupt();//主线程打断t1线程
    log.debug(" 打断状态: {}", t1.isInterrupted());//输出打断标记
}

输出

java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at cn.itcast.n2.util.Sleeper.sleep(Sleeper.java:8)
at cn.itcast.n4.TestInterrupt.lambda$test1$3(TestInterrupt.java:59)
at java.lang.Thread.run(Thread.java:745)
21:18:10.374 [main] c.TestInterrupt - 打断状态: false

打断正常运行的线程

打断正常运行的线程, 不会清空打断状态,也就是打断状态会变成true

private static void test2() throws InterruptedException {
    Thread t2 = new Thread(()->{
        while(true) {
            Thread current = Thread.currentThread();
            boolean interrupted = current.isInterrupted();
            if(interrupted) {
                log.debug(" 打断状态: {}", interrupted);
                break;
            }
        }
    }, "t2");
    t2.start();
    sleep(0.5);
    t2.interrupt();
}

输出

20:57:37.964 [t2] c.TestInterrupt - 打断状态: true

如果只写一个 while true ,里面没有判断的话,那么这个主线程是打断不了t2线程的。

因为interrupt实际上不能打断另一个线程,只是告诉那个线程:我要打断你,被打断的线程自己决定受不受其他线程打断。也就是将打断状态改成true,说明有其他线程要打断我。

所以可以在里面加一个if判断,如果interrupted是真,说明有其他线程要打断我,所以我自己结束好了。

打断park线程

打断 park 线程, 不会清空打断状态,也就是打断状态是true

例如下面的代码,主线程休眠之后,打断park线程

private static void test3() throws InterruptedException {
   Thread t1 = new Thread(() -> {
      log.debug("park...");
      LockSupport.park();
      log.debug("unpark...");
      log.debug("打断状态:{}", Thread.currentThread().isInterrupted());
   }, "t1");
   t1.start();
   sleep(0.5);
   t1.interrupt();
}

输出

21:11:52.795 [t1] c.TestInterrupt - park...

21:11:53.295 [t1] c.TestInterrupt - unpark...

21:11:53.295 [t1] c.TestInterrupt - 打断状态:true

如果打断标记已经是 true, 则 park 会失效 

private static void test4() {
   Thread t1 = new Thread(() -> {
      for (int i = 0; i < 5; i++) {
         log.debug("park...");
         LockSupport.park();
         log.debug("打断状态:{}", Thread.currentThread().isInterrupted());
      }
   });
   t1.start();
   sleep(1);
   t1.interrupt();
}

输出

21:13:48.783 [Thread-0] c.TestInterrupt - park...
21:13:49.809 [Thread-0] c.TestInterrupt - 打断状态:true
21:13:49.812 [Thread-0] c.TestInterrupt - park...
21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true
21:13:49.813 [Thread-0] c.TestInterrupt - park...
21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true
21:13:49.813 [Thread-0] c.TestInterrupt - park...
21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true
21:13:49.813 [Thread-0] c.TestInterrupt - park...
21:13:49.813 [Thread-0] c.TestInterrupt - 打断状态:true

清除打断标记

可以使用 Thread.interrupted() 清除打断状态,清除打断状态就是让打断状态变成false,意思是这个线程没有被打断

不推荐使用的方法

这些方法已过时,容易破坏同步代码块,造成线程死锁

jdk源码中也有说,不推荐这些方法

  1. stop():停止线程运行
  2. suspend():挂起(暂停)线程运行
  3. resume() :恢复线程运行

守护线程

默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。

有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。

守护线程的例子

  1. java的垃圾回收器线程就是一种守护线程。
  2. Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求

将某个线程设置为守护线程的方法

thread.setDaemon(true);

log.debug("开始运行...");
   Thread t1 = new Thread(() -> {
      log.debug("开始运行...");
      sleep(2);
      log.debug("运行结束...");
   }, "daemon");
// 设置该线程为守护线程
t1.setDaemon(true);
t1.start();
   sleep(1);
log.debug("运行结束...");

输出

08:26:38.123 [main] c.TestDaemon - 开始运行... 
08:26:38.213 [daemon] c.TestDaemon - 开始运行... 
08:26:39.215 [main] c.TestDaemon - 运行结束...

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

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

相关文章

程序员必读十大电子书

程序员必读十大电子书 云原生架构白皮书 2022 版程序员面试宝典Java 工程师成神之路微服务治理技术白皮书Java 开发手册&#xff08;嵩山版&#xff09;必致&#xff08;BizDevOps&#xff09;白皮书 2022Elasticsearch 全观测技术解析与应用&#xff08;构建日志、指标、APM 统…

Tik Tok海外公会是什么?

在数字社交媒体领域&#xff0c;TikTok已成为全球性的热门应用之一&#xff0c;印尼市场也不例外。作为全球第四人口最多的国家&#xff0c;印尼的年轻人口众多&#xff0c;是一个极具潜力的市场。对于希望在印尼市场进行TikTok公会申请的机构来说&#xff0c;了解市场发展趋势…

NLP实验案例100个(1-5)

实验一 array数组&#xff08;01&#xff09; 一、实验目的及要求 1.安装numpy环境&#xff0c;掌握基本的数组知识以及操作。 二、实验设备&#xff08;环境&#xff09;及要求 开发环境&#xff1a;jupyter notebook 开发语言以及相关的库&#xff1a;python开发语言、nu…

微信小程序:授权登录获取手机号及获取基本信息等

1、授权获取code&#xff0c;换取open_id wx.login({success(res) {console.log(res.code)if (res.code) {//调自己后台接口换取open_id}} })微信文档 2、获取手机号 1&#xff09;需要将 button 组件 open-type 的值设置为 getPhoneNumber&#xff0c;当用户点击并同意之后…

大采购3.0,打造企业采购数智化的韧性变革!

大采购3.0&#xff0c;深化采购全链路管控&#xff0c;聚焦数智化运营、智慧化监管、个性化需求适配、一体化协同、稳定可靠、安全可信、企业级服务七大核心基因&#xff0c;围绕采购管理、供应商全生命周期管理、人工智能深入应用、易用性、交付能力等方面进行了全面升级和优化…

利用MATLAB制作DEM山体阴影

在地理绘图中&#xff0c;我们使用的DEM数据添加山体阴影使得绘制的图件显得更加的美观。 GIS中使用ArcGIS软件就可以达到这一目的&#xff0c;或者使用GMT&#xff0c;同样可以得到山体阴影的效果。 本文提供了一个MATLAB的函数&#xff0c;可以得到山体阴影。 clear all;c…

【代码随想录day21】二叉搜索树的最近公共祖先

题目 思路 解题的关键是知道自顶向低递归遍历&#xff0c;第一次遇到root在p和q的区间中时&#xff0c;则root就是p和q的最近公共祖先节点。 递归法 # Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val x # …

后端性能测试的类型

目录 性能测试的类型 负载测试(load testing) 压力测试(Stress Testing) 可扩展性测试( 尖峰测试(Spike Testing) 耐久性测试(Endurance Testing) 并发测试(Concurrency Testing) 容量测试(Capacity Testing) 资料获取方法 性能测试的类型 性能测试&#xff1a;确定软…

QT:手动实现登录框

要求&#xff1a; 1、登录窗口更改标题、图标 2、设置固定尺寸、并给定一定的透明度 #include "mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) {this->setFixedSize(800,650); //设置固定尺寸qDebug()<<this->windowT…

新一代分布式任务调度框架

概述 PowerJob是新一代分布式任务调度与计算框架&#xff0c;支持CRON、API、固定频率、固定延迟等调度策略&#xff0c;提供工作流来编排任务解决依赖关系&#xff0c;能让您轻松完成作业的调度与繁杂任务的分布式计算。 为什么选择PowerJob&#xff1f; 当前市面上流行的作…

盒式外观安装比例放大器

比例阀放大器是一种电子放大器&#xff0c;可以将微小的电信号放大成较大的电信号。比例阀放大器通常用于工业控制系统中&#xff0c;其特点是可以调节控制参数&#xff0c;从而可以控制系统的行为。比例阀放大器有板式、盒式、插头式和集成式四种类型&#xff0c;每种类型都有…

嵌入式Linux系统组成

嵌入式Linux系统的组成 文章目录 嵌入式Linux系统的组成一、发行版Linux系统VS嵌入式Linux系统二、嵌入式Linux系统架构一、发行版Linux系统VS嵌入式Linux系统 1.产品 发行版Linux系统产品:服务器、消费平板、消费手提电脑 嵌入式Linux系统产品:扫地机器人,小米机顶盒特定场…

「预告」飞凌嵌入式邀您相约第13届配电技术应用论坛

2023年8月3日~5日&#xff0c;第十三届配电技术应用论坛即将在浙江杭州举行&#xff0c;飞凌嵌入式受邀参加。 作为助力快速实现“双碳”目标和新型电力系统建设&#xff0c;加强“双碳”目标下的智能配电网技术研发布局的主要会议&#xff0c;第十三届配电技术应用论坛将从政…

无涯教程-jQuery - jQuery.getScript( url, callback )方法函数

jQuery.getScript(url&#xff0c;[callback])方法使用HTTP GET请求加载并执行JavaScript文件。 该方法返回XMLHttpRequest对象。 jQuery.getScript( url, [callback] ) - 语法 $.getScript( url, [callback] ) 这是此方法使用的所有参数的描述- url - 包含请求…

谁懂啊!这个教室神器居然轻松搞定课堂纪律

教育是人类社会发展进步的重要支柱&#xff0c;而教师则是这个伟大事业的奠基者。随着科技的飞速发展&#xff0c;我们正迎来教育领域全新的可能性。 在这个数字化时代&#xff0c;在线巡课系统成为了现代教育管理的一颗明星&#xff0c;为教育者提供了更加高效、精确的教学评估…

Redis复制 (replica)

是什么 官网地址&#xff1a;Redis replication | Redis 其实就是主从复制&#xff0c;Master以写为主&#xff0c;Slave以读为主&#xff0c;当master数据变化的时候&#xff0c;自动将新的数据异步同步到其它slave数据库。 能干嘛 读写分离容灾恢复数据备份水平扩容支撑高并…

无涯教程-jQuery - load( url, data, callback)方法函数

load(url&#xff0c;data&#xff0c;callback)方法从服务器加载数据&#xff0c;并将返回的HTML放入匹配的元素中。 load( url, [data], [callback] ) - 语法 [selector].load( url, [data], [callback] ) 这是此方法使用的所有参数的描述- url - 包含请求发送到…

不需要PS也能生成淘宝我的订单页面截图

不知道大家有没有遇到这样的情况&#xff1a;在发微博、发朋友圈或者写博客的时候&#xff0c;想要分享购物心得&#xff0c;但却苦恼于找不到虚拟淘宝订单截图&#xff1f;别担心&#xff0c;今天我就来教大家一个轻松又快捷的方法——使用淘宝订单生成器。 无需PS&#xff0c…

uni-app踩坑记

打包h5如何配置域名&#xff1a; 在manifest.json中配置域名 配置完成后无论是测试环境还是正式环境都带上/mobile/&#xff0c;否则会报错404 如何引入调试工具erada: 在默认的index.html中直接引入erada&#xff0c;页面样式会整个错乱&#xff0c;解决方案就是引入官方…

Ueditor 百度强大富文本Springboot 项目集成使用(包含上传文件和上传图片的功能使用)简单易懂,举一反三

Ueditor 百度强大富文本Springboot 项目集成使用 首先如果大家的富文本中不考虑图片或者附件的情况下&#xff0c;只考虑纯文本且排版的情况下我们可以直接让前端的vue来继承UEditor就可以啦。但是要让前端将那几个上传图片和附件的哪些功能给阉割掉&#xff01; 然后就是说如…