Java线程——常见方法

news2024/11/25 11:29:01

一、 常见方法

1.1 概述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

start_vs_run:直接调用run方法并不会启动新的线程

import cn.itcast.n2.util.FileReader;
import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test")
public class Test {
    public static void main(String[] args) {
        Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                log.debug("running");
                FileReader.read(Constants.MP4_FULL_PATH);
            }
        };

        t1.run();
        log.debug("do other things......");
    }
}

执行结果:直接调用run方法并不会启动新的线程, FileReader.read() 方法调用还是同步的,还是在主线程中调用run,并不能达到提升性能或者异步处理的效果,因此启动一个线程必须要start()。start()启动了线程,再由新的线程去调用run()方法
在这里插入图片描述
结论
● 直接调用 run 是在主线程中执行了 run,没有启动新的线程
● 使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码

sleep与yield

sleep

  1. 调用 sleep 会让当前线程从 Running 进入 Timed Waiting (有时限的等待)状态(阻塞
  2. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出InterruptedException
  3. 睡眠结束后的线程未必会立刻得到执行
  4. 建议用 TimeUnit(1.5以后新增) 的 sleep 代替 Thread 的 sleep 来获得更好的可读性(其内部也是调用了sleep方法,但其进行了单位的换算使程序可读性更好)

yield

  1. 调用 yield(让出CPU的使用权) 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
  2. 具体的实现依赖于操作系统的任务调度器

sleep是让线程进入Timed Waiting(阻塞)状态,yield是让线程进入 Runnable(就绪)状态,两个状态表面上都是让线程先不运行,将机会让给其他线程。两者的区别是:
Runnable(就绪)状态:还是有机会被任务调度器调度(任务调度器还是会分时间片给就绪状态的线程)
Timed Waiting(阻塞)状态:任务调度器不会将时间片分给阻塞状态的线程

程序优先级

Thread类中的setPriority()可以设置优先级
在这里插入图片描述

● 线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
● 如果 cpu 比较繁忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用

yield与线程优先级并不是很容易控制线程的调度

yield操作示例:(如果以下两个线程同时运行并且被CPU调度的机会均等,则最终的打印结果count值较为接近)

1. 不加任何控制

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test9")
public class Test9 {

    public static void main(String[] args) {
        //  任务对象1(死循环)
        Runnable task1 = () -> {
            int count = 0;
            for (;;) {
                System.out.println("---->1 " + count++);
            }
        };
        
        //  任务对象2(死循环)
        Runnable task2 = () -> {
            int count = 0;
            for (;;) {
//                Thread.yield();
                System.out.println("              ---->2 " + count++);
            }
        };
        Thread t1 = new Thread(task1, "t1");
        Thread t2 = new Thread(task2, "t2");
  //      t1.setPriority(Thread.MIN_PRIORITY);
  //      t2.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        t2.start();
    }
}

被CPU调度机会大致形同
在这里插入图片描述

2. 线程2开启yield()===>线程1得到的时间片就较多进而导致线程1数字增长就较为快些
在这里插入图片描述
线程1、2增长效果差异较为明显

3. 使用优先级,将线程1的优先级设置较低,线程2的优先级较高
在这里插入图片描述
并没有明显效果

总结:无论是yield()还是优先级,他们仅仅是对调度器的一个提示,均不能真正去控制线程任务的调度。最终还是由操作系统的任务调度器来决定哪个线程获得更多的时间片

join

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

static int r = 0;
public static void main(String[] args) throws InterruptedException {
 test1();
}

private static void test1() throws InterruptedException {
 log.debug("开始");
 Thread t1 = new Thread(() -> {
 log.debug("开始");
 // 线程1睡眠1秒
 sleep(1);
 log.debug("结束");
 r = 10;
 });
 
 t1.start();
 log.debug("结果为:{}", r);
 log.debug("结束");
}

执行结果:
在这里插入图片描述

分析
● 因为主线程和线程 t1 是并行执行的,t1 线程需要 1 秒之后才能算出 r=10
● 而主线程一开始就要打印 r 的结果,所以只能打印出 r=0

解决方法
● 用 sleep 行不行?为什么?
● 用 join,加在 t1.start() 之后即可
在这里插入图片描述

join-同步应用(案例1)

以调用方角度来讲,如果
● 需要等待结果返回,才能继续运行就是同步(上述例子中主线程需要同步等待t1线程结果返回)
● 不需要等待结果返回,就能继续运行就是异步

问,下面代码 cost 大约多少秒?

static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
 test2();
}
private static void test2() throws InterruptedException {

 Thread t1 = new Thread(() -> {
 sleep(1);
 r1 = 10;
 });
 
 Thread t2 = new Thread(() -> {
 sleep(2);
 r2 = 20;
 });
 
 long start = System.currentTimeMillis();
 t1.start();
 t2.start();
 t1.join();
 t2.join();
 long end = System.currentTimeMillis();
 log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}

执行结果:(时间差大约为2秒)
在这里插入图片描述

分析如下
● 第一个 join:等待 t1 时, t2 并没有停止, 而在运行
● 第二个 join:1s 后, 执行到此, t2 也运行了 1s, 因此也只需再等待 1s

有时效的 join

1. 未等够时间

import lombok.extern.slf4j.Slf4j;
import static cn.itcast.n2.util.Sleeper.sleep;

@Slf4j(topic = "c.Test")
public class Test {
    static int r1 = 0;
    static int r2 = 0;

    public static void main(String[] args) throws InterruptedException {
        test3();
    }

    public static void test3() throws InterruptedException {

        Thread t1 = new Thread(() -> {
            sleep(2);
            r1 = 10;
        });

        long start = System.currentTimeMillis();
        t1.start();

        log.debug("join begin");
        // 主线程等待t1运行结束(最多等待1.5s)
        t1.join(1500);
        long end = System.currentTimeMillis();
        log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
    }
}

运行结果:(1.5s后主线程便不再等待)

在这里插入图片描述

2. 等够时间

将 t1.join(1500)修改为 t1.join(3000);

运行结果:
在这里插入图片描述
如果线程提前结束,join()也会提前结束,并不会按最大时间等待;若未等够时间,则按时间结束

interrupt-打断

1. 打断阻塞状态的线程
● 打断 sleep,wait,join(这几个方法都会让线程进入阻塞状态) 这些处于阻塞状态的线程,也可以用来打断正在运行状态的线程
* join的底层原理也为wait

打断 sleep 的线程, 会清空打断标记,以 sleep 为例

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test11")
public class Test11 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("sleep...");
            try {
                Thread.sleep(5000); // wait, join
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t1");

        t1.start();
        // 打断正在睡眠的线程,否则t1还未进入睡眠打断的为正常运行下的线程
        Thread.sleep(1000);
        log.debug("interrupt");
        t1.interrupt();
        // 正常情况下打断标记为ture,反之为false
        log.debug("打断标记:{}", t1.isInterrupted());
    }
}

运行结果:
在这里插入图片描述

阻塞状态的线程被打断后会抛出异常(InterruptedException),打断后还会有一个打断标记(布尔值),表示当前线程是否被其他线程所打断干扰过,但sleep,wait,join它们在被打断后都会将打断标记清空(重置为false)

2. 打断正常运行的线程

打断正常运行的线程,不会打断清空状态

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.Test12")
public class Test12 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while(true) {
            // 获取当前线程(t1),判断是否被打断
                boolean interrupted = Thread.currentThread().isInterrupted();
                if(interrupted) {
                    log.debug("被打断了, 退出循环");
                    break;
                }
            }
        }, "t1");
        t1.start();

        Thread.sleep(1000);
        log.debug("interrupt");
        // 主线程1s后打断t1线程
        t1.interrupt();
    }
}

运行结果:
在这里插入图片描述
打断状态可以用来停止线程

1.2 sleep应用案例(防止CPU占用100%)

sleep实现

while(true)应用:===>在做服务器开发时,编写服务端应用程序需要线程不断运行进而一直接收请求、访问、响应

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

  while (true){
       try {
       // 防止不断利用CPU计算,空转浪费CPU资源(防止对CPU资源的占用)
           Thread.sleep(50);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }

● 可以用wait或条件变量达到类似的效果
● 不同的是,后两种都需要加锁,并且需要响应的唤醒操作,一般适用于要进行同步的场景
● sleep使用于无需锁同步的场景

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

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

相关文章

【网络安全】Windows系统安全实验

第3模块 Windows操作系统安全部分 3.1 帐户和口令的安全设置 3.1.1 实验目的 本章实验的目的主要是熟悉Windows操作系统中帐户和口令的安全设置,掌握删除、禁用帐户的方法,了解并启用密码策略和用户锁定策略,体验查看“用户权限分配”、…

如何在Linux环境下用VI编辑器写C程序编译C程序运行C程序

我是荔园微风,作为一名在IT界整整25年的老兵,今天我们来重点说一说如何在Linux环境下用VI编辑器写C程序编译C程序运行C程序。相信大家在Windows环境下写C程序编译C程序运行C程序怎么弄都已经很清楚了,现在我们来看在Linux下如果来做&#xff…

Vue 在for循环中动态添加类名及style样式集合

介绍 在vue的 for 循环中,经常会使用到动态添加类名或者样式的情况,实现给当前的选中的 div 添加不同的样式。 动态添加类名 提示: 所有动态添加的类名,放在表达式里都需要添加引号,进行包裹。 通过 对象 的形式&a…

Git ---- Git 分支操作

Git ---- Git 分支操作1. 什么是分支2. 分支的好处3. 分支的操作1. 查看分支2. 创建分支3. 修改分支4. 切换分支5. 合并分支5. 产生冲突4. 创建分支和切换分支图解1. 什么是分支 在版本控制过程中,同时推进多个任务,为每个任务,我们就可以创…

【已解决】安装cv2时Building wheel for opencv-python终端卡死

目录1 问题背景2 问题探索3 问题解决4 告别Bug1 问题背景 环境: Ubuntu20.04Python3.6 现象:终端输出类似下面的命令,并卡住不动 Building wheel for opencv-python (PEP 517) ... 2 问题探索 首先,OpenCV较旧的Python版本不需要…

什么是自动化运维?为什么选择Python做自动化运维?

“Python自动化运维”这个词,想必大家都听说过,但是很多人对它并不了解,也不知道是做什么的,那么你对Python自动化运维了解多少呢?跟着蛋糕往下看。 什么是Python自动化运维? 随着技术的进步、业务需求的快速增长,…

网友爆料奇葩leader:日报要精确到0.5小时,每晚检查!每周写周计划,评审ABCD等级,午休不许刷手机、看视频、玩游戏!...

这样的leader你能接受吗?一位网友吐槽:一个团队6个人,加上leader一共7个人。leader要求每天写日报,并且要精确到0.5小时,格式必须正确,每天晚上都看,下班后各种问进度。每周一要写周计划&#x…

IM即时通讯构建企业协同生态链

在当今互联网信息飞速发展的时代,随着企业对协同办公要求的提高,协同办公的定义提升到了智能化办公的范畴。大多企业都非常重视构建连接用户、员工和合作伙伴的生态平台,利用即时通讯软件解决企业内部的工作沟通、信息传递和知识共享等问题。…

Pytorch 网络结构的可视化

在构建网络的过程中,需要查看网络结构,以便于优化,使用Pytorch常用的可视化工具有 1.Hidden layer myNet U_Net() print(myNet)# ## 可视化卷积神经网络,MyConvnet是定义的神经网络结构 hl_graph hl.build_graph(myNet, torch.zeros([1, 3…

国产蓝牙耳机哪个好用?国产好用的蓝牙耳机推荐

现如今,国产蓝牙耳机越来越受到人们关注,国产蓝牙耳机近几年的发展愈发迅猛,配置上相对于非国产蓝牙耳机来说也毫不逊色。那么,国产蓝牙耳机哪个好用?下面,我来给大家推荐几款好用的蓝牙耳机,一…

电脑应用程序在c盘怎么转移到d盘?建议先收藏

电脑应用程序在c盘怎么转移到d盘?很多小伙伴第一反应就是直接剪切,这种方法对于普通文件来说很简单、也很方便,只需执行CtrlX就能办到,然而对于已安装的应用程序,这并不是明智的做法。因为直接剪切粘贴后,应…

【概念辨析】二维数组传参的集中可能性

一、二维数组传参竟然不是用二级指针进行接收? 今天进行再一次的二级指针学习时,发现了一条以前没怎么注意过的知识点:二维数组进行传参只能用二维数组(不能省略列)进行接收或者是数组指针。 问题复现代码如下&#xf…

ChatGPT 编写模式:如何高效地将思维框架赋予 AI ?

如何理解 Prompt ?Prompt Enginneeringprompt 通常指的是一个输入的文本段落或短语,作为生成模型输出的起点或引导。prompt 可以是一个问题、一段文字描述、一段对话或任何形式的文本输入,模型会基于 prompt 所提供的上下文和语义信息&#x…

现有安全技术

现有的安全技术 防火墙技术 数据机密性技术 防火墙技术比较静态地保护网络支付涉及的客户端网络商家网络、金融专用网络等网络级的安全,数据传输过程中的安全性则需要数据机密性技术进一步给以保护 我们将源信息称为明文。为了保护明文,将其通过某种…

LMR23630APQDRRTQ1应用INA2181A1QDGSRQ1电流检测放大器电路图

LMR23630/LMR23630-Q1 SIMPLE SWITCHER降压转换器是易于使用的36V、3A同步降压稳压器。该器件的宽输入电压范围为 4.5V 至 36V,适用于调节从工业到汽车等各类应用中非稳压电源的电源调理。采用了峰值电流模式控制,以实现对环路补偿和逐周期电流限制的简单…

Docker 容器命令 和安装各种镜像环境

CentOS安装Docker 1.1.卸载(可选) 如果之前安装过旧版本的Docker,可以使用下面命令卸载: yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotat…

Qt-FFmpeg开发-保存视频流裸流(11)

Qt-FFmpeg开发-保存视频流裸流📀 文章目录Qt-FFmpeg开发-保存视频流裸流📀1、概述📸2、实现效果💽3、FFmpeg保存裸流代码流程💡4、主要代码🔍5、完整源代码📑更多精彩内容👉个人内容…

【HTML】我的花儿我做主~ 指定花瓣的数量,生成花朵~

效果图 前言(赛时灵感) 在比赛开始前,就一直没有灵感,不知道参与哪个赛道。也还不知道用啥去做。 直到比赛开始,还是没想到。 最后在比赛快要结束的第五天三更半夜的时候; 突然想到,既然要浪漫&…

每日学术速递2.23

Subjects: Robotics 1.On discrete symmetries of robotics systems: A group-theoretic and data-driven analysis ​ 标题:关于机器人系统的离散对称性:群论和数据驱动分析 作者:Daniel Ordonez-Apraez, Mario Martin, Antonio Agudo, F…

python中的取整、四舍五入和输出小数点后n位

各种取整 1.int是向下取整 2.math.ceil(num)是向上取整 3.math.floor(num)是向下取整 4.round(num)一般情况下是四舍五入取整(round毛病多) 四舍五入 1.round()可以四舍五入 2.int()也可以 这样写(数后面0.5)&#xff1a…