(二)Java 线程

news2024/11/27 18:44:38

一、创建和运行线程

1. 方法一,直接使用 Thread

@Slf4j(topic = "c.Test1")
public class Demo {
    public static void main(String[] args) {
        Thread t = new Thread(){
            @Override
            public void run() {
                log.debug("running");
            }
        };
        t.setName("t1");
        t.start();
    }

}

2. 方法二,使用 Runnable 配合 Thread

把【线程】和【任务】(要执行的代码)分开
(1)Thread 代表线程
(2)Runnable 可运行的任务(线程要执行的代码)
@Slf4j(topic = "c.Test2")
public class Test2 {
    public static void main(String[] args) {
        Runnable r = () -> log.debug("running");

        Thread t = new Thread(r, "t2");

        t.start();
    }
}

3. 原理之 Thread Runnable 的关系

小结
(1)方法 1 是把线程和任务合并在了一起,方法 2 是把线程和任务分开了
(2)用 Runnable 更容易与线程池等高级 API 配合
(3)用 Runnable 让任务类脱离了 Thread 继承体系,更灵活

4. 方法三,FutureTask 配合 Thread

FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况
@Slf4j(topic = "c.Test1")
public class Demo {
    public static void main(String[] args) {
        FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                log.debug("running...");
                Thread.sleep(2000);
                return 100;
            }
        });

        Thread t1 = new Thread(task, "t1");
        t1.start();
    }
}

二、查看进程线程的方法

1. windows

任务管理器可以查看进程和线程数,也可以用来杀死进程
(1)tasklist: 查看进程

(2)taskkill: 杀死进程

  

2. linux

(1)ps -fe :查看所有进程

(2)ps - fT - p <PID> 查看某个进程( PID )的所有线程
(3)kill : 杀死进程

(4)top 按大写 H 切换是否显示线程
(5)top - H - p <PID> 查看某个进程( PID )的所有线程

3. Java

(1)jps : 命令查看所有 Java 进程
(2)jstack <PID> : 查看某个 Java 进程( PID )的所有线程状态
(3)jconsole : 来查看某个 Java 进程中线程的运行情况(图形界面)

4. jconsole 远程监控配置

需要以如下方式运行你的 java
java -Djava.rmi.server.hostname=`ip地址` -Dcom.sun.management.jmxremote -
Dcom.sun.management.jmxremote.port=`连接端口` -Dcom.sun.management.jmxremote.ssl=是否安全连接 -
Dcom.sun.management.jmxremote.authenticate=是否认证 java类

三、常见方法

1. start run

(1)直接调用 run 是在主线程中执行了 run,没有启动新的线程

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

2. sleep yield

2.1 sleep

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

(2)其他线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException

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

(4)建议用 TimeUnit 的 sleep 代替 Thread 的sleep 来获得更好的可读性

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

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

2.2 yield

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

(2)具体的实现依赖于操作系统的任务调度器

2.3 线程优先级

(1)线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它。

(2)如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲,优先级几乎没作用。

3. join 方法详解

3.1 为什么需要 join

@Slf4j(topic = "c.Test10")
public class Test10 {
    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("开始");
            sleep(1);
            log.debug("结束");
            r = 10;
        },"t1");
        t1.start();
        t1.join();
        log.debug("结果为:{}", r);
        log.debug("结束");
    }
}
分析
(1)因为主线程和线程 t1 是并行执行的, t1 线程需要 1 秒之后才能算出 r=10
(2)而主线程一开始就要打印 r 的结果,所以只能打印出 r=0
解决方法
(1) join ,加在 t1.start() 之后即可

3.2 等待多个结果

3.3 有时效的 join 

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

4. interrupt 方法详解

4.1 打断 sleep、wait、join 的线程

@Slf4j
public class Demo {

    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();
        Thread.sleep(1000);
        log.debug("interrupt");
        t1.interrupt();
        log.debug("打断标记:{}", t1.isInterrupted());//打断标记:false
    }
}

4.2 打断正常运行的线程

@Slf4j
public class Demo {

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

        Thread.sleep(1000);
        log.debug("interrupt");
        t1.interrupt();
    }
}

打断正常运行的线程, Thread.currentThread().isInterrupted() 会变成 true 。

4.3 模式之两阶段终止

在一个线程 T1 中如何 优雅 终止线程 T2
这里的【优雅】指的是给 T2 一个料理后事的机会。

错误思路:

(1)使用线程对象的 stop() 方法停止线程

        stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其他线程将永远无法获取锁。

(2)使用 System.exit(int) 方法停止线程

        目的仅是停止一个线程,但这种做法会让整个程序都停止。

 

 

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

    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination tpt = new TwoPhaseTermination();
        tpt.start();

        Thread.sleep(3500);
        tpt.stop();
    }


}

@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination{
    private Thread monitor;

    // 启动监控线程
    public void start(){
        monitor = new Thread(()->{
            while (true){
                Thread current = Thread.currentThread();
                if (current.isInterrupted()){
                    log.debug("料理后事");
                    break;
                }
                try {
                    Thread.sleep(1000);
                    log.debug("执行监控记录");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // 重新设置打断标记
                    current.interrupt();
                }

            }
        });

        monitor.start();
    }

    // 停止监控线程
    public void stop(){
        monitor.interrupt();
    }
}

4.4 打断 park 线程

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

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


        sleep(1);
        t1.interrupt();
    }

    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(1);
        t1.interrupt();

    }

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

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

test4:可以使用 Thread.interrupted() 清除打断状态。Thread.interrupted()会重置打断标记为 false

4.5 不推荐的方法

还有一些不推荐使用的方法,这些方法已过时,容易破坏同步代码块,造成线程死锁

 

四、主线程与守护线程

默认情况下, Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。
@Slf4j(topic = "c.Test15")
public class Test15 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    break;
                }
            }
            log.debug("结束");
        }, "t1");
        t1.setDaemon(true);
        t1.start();

        Thread.sleep(1000);
        log.debug("结束");
    }
}
注意
(1)垃圾回收器线程就是一种守护线程
(2)Tomcat 中的 Acceptor Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求

五、五种状态

这是从 操作系统 层面来描述的
(1)【初始状态】仅是在语言层面创建了线程对象,还未与操作系统线程关联
(2)【可运行状态】(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由 CPU 调度执行
(3)【运行状态】指获取了 CPU 时间片运行中的状态
1️⃣当 CPU 时间片用完,会从【运行状态】转换至【可运行状态】,会导致线程的上下文切换
(4)【阻塞状态】
1️⃣如果调用了阻塞 API ,如 BIO 读写文件,这时该线程实际不会用到 CPU,会导致线程上下文切换,进入【阻塞状态】
2️⃣等 BIO 操作完毕,会由操作系统唤醒阻塞的线程,转换至【可运行状态】
3️⃣与【可运行状态】的区别是,对【阻塞状态】的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们
(5)【终止状态】表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态

 

六、六种状态

这是从 Java API 层面来描述的
根据 Thread.State 枚举,分为六种状态
(1)NEW 线程刚被创建,但是还没有调用 start() 方法
(2)RUNNABLE 当调用了 start() 方法之后,注意, Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的 【可运行状态】、【运行状态】和【阻塞状态】(由于 BIO 导致的线程阻塞,在 Java 里无法区分,仍然认为是可运行)
(3)BLOCKED WAITING TIMED_WAITING 都是 Java API 层面对【阻塞状态】的细分,后面会在状态转换一节详述
(4)TERMINATED 当线程代码运行结束

 

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

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

相关文章

Casein-PEG-Indocyanine green 络蛋白-聚乙二醇-吲哚菁绿 Casein-ICG

产品名称&#xff1a;络蛋白-聚乙二醇-吲哚菁绿 英文名称&#xff1a;Casein-PEG-Indocyanine green 质量控制&#xff1a;95% 原料分散系数PDI&#xff1a;≤1.05 存储条件&#xff1a;-20C&#xff0c;避光&#xff0c;避湿 用 途&#xff1a;仅供科研实验使用&#xff0c;…

【免杀前置课——Windows编程】十三、事件与信号量——事件与互斥体区别、操纵信号量实现游戏多开访问控制(附代码)

事件 事件可以完全控制&#xff0c;其他无法控制线程的执行顺序&#xff0c;但是事件对象可以做到。 ***事件(Event&#xff09;***是在线程同步中最常使用的一种同步对象&#xff0c;事件包含一个使用计数&#xff0c;一个是用来表示自动重置/手动重置的布尔值&#xff0c;另…

[附源码]计算机毕业设计springboot高校后勤保障系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[附源码]计算机毕业设计springboot个性化名片网站

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

MySQL的索引与事务

目录 1.索引的本质 2.索引的使用 2.1查看索引 2.2创建索引 2.3删除索引 3.索引的数据结构 3.1B树 3.2B树 4.事务 4.1 事物的回滚(rollback) 4.2 事务的四大特性(ACID) 4.2.1 原子性 4.2.2 一致性 4.2.3 持久性 4.2.4 隔离性 5.并发引起的问题 5.1 "读脏数…

IDEA中debug启动报错Method breakpoints may dramatically slow down debugging

1.原因:是因为我们打断点太多了,可能在mapper或者service层打了断点导致启动失败 解决方案:1.去掉所有我们打的断点, 2.去掉mapper或者service中我们打的错误断点

学习swagger,使用正则改造项目, 生成接口文档

学习swagger&#xff0c;使用正则改造项目 关于SwaggerKnife4j生成统一接口文档教程请点击以下关于SwaggerKnife4j生成统一接口文档 1 关于构建swagger文档所需要的依赖和配置类 SwaggerKnife4j - 统一接口文档 我们以某一个项目的swagger升级改造为例。 2 如何使用正则表达…

Discourse 的左侧边栏可以修改吗

在默认的 Discourse 配置中&#xff0c;我们左侧的边栏可以根据自己的要求进行修改吗&#xff1f; 解决办法 针对自己登录的用户&#xff0c;你是可以自己调整左侧边栏的配置。 单击右上角你的个人头像&#xff0c;然后选择属性。 在切换的界面中&#xff0c;选择属性。 在出…

Postman内置动态参数和自定义的动态参数以及断言方式

一、问题&#xff1a;每次请求均需手动修改参数 解决方案&#xff1a;使用动态参数&#xff1a;内置动态参数/自定义动态参数&#xff0c;解决上述问题 二、Postman动态参数 1、内置动态参数&#xff0c;表现形式&#xff1a;{{$}} 2、常用的内置动态参数 {{$timestamp}} …

基于模糊小波神经网络的空中目标威胁评估(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 在现代战争中, 随着信息化和智能化的飞速发展, 以及作战环境的日益复杂, 实时而准确地评估目标威胁, 不仅为空战决策提供科学的…

最全面的Spring教程(四)——Controller 与 RestFul

前言 本文为 【SpringMVC教程】Controller 与 RestFul 相关内容介绍&#xff0c;具体将对控制器Controller&#xff0c;实现Controller接口&#xff0c;使用注解Controller&#xff0c;RequestMapping及RestFul 风格&#xff08;包括&#xff1a;Rest架构的主要原则、什么是Res…

微服务框架 SpringCloud微服务架构 6 Nacos 配置管理 6.2 微服务配置拉取

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 SpringCloud微服务架构 文章目录微服务框架SpringCloud微服务架构6 Nacos 配置管理6.2 微服务配置拉取6.2.1 统一配置管理6.2.2 直接开干6.2…

从电商到超市,美团的零售之变

从上海回到湖南长沙县的时候&#xff0c;何靓做好了过“苦日子”的准备。作为一个湖南人&#xff0c;她知道县城往往意味着没有星巴克和喜茶&#xff0c;意味着仅有的一两座电影院环境不太好&#xff0c;意味着每天晚上九点后连便利店都大门紧闭。 但在真正回到这“半个故土”…

Cloudcomplare标注3D分割数据

免安装软件下载 https://www.sibspress.org/soft/23705.html 可以打开txt&#xff0c; pcd&#xff0c; ply等3D点云数据 将文件直接拖进软件内打开 选择剪切工具&#xff1a;剪刀&#xff0c;并设置线标注工具 鼠标左键选择区域&#xff0c; 鼠标右键结束选择 选择圈内或…

没睡醒就来上班的程序员解决BUG

仅以此篇纪念我在低级错误面前烦躁又蒙B的3分钟。 今早&#xff0c;我在公司系统上点了一下我负责模块里的一个查询。我靠&#xff0c;这个查询条件竟然没生效。 看代码&#xff0c;原来xml文件里的SQL没写这个查询条件&#xff0c;这太简单了&#xff0c;加上。&#xff09;…

vue - vue使用echarts实现中国地图和点击省份进行查看

文章目录1&#xff0c;实现的效果和功能2&#xff0c;安装ECharts3&#xff0c;main.js里面引入echarts4&#xff0c;实现如下5&#xff0c;遇到的问题6&#xff0c;用到的模拟数据1&#xff0c;实现的效果和功能 vue使用echarts实现中国地图和点击省份进行查看&#xff1b; 下…

嵌入式开发--Altium技巧:原理图设置

Altium的默认设置&#xff0c;有一些是很不合理的选项&#xff0c;强烈建议大家修改。 在工程上点右键&#xff0c;并在弹出的菜单上选择最后一项&#xff1a;Project Options&#xff0c;即项目设置 弹出如下界面&#xff0c;如果操作不当&#xff0c;设置错了&#xff0c;可…

STM32 bit-band位带操作

在51单片机中&#xff0c;我们可以通过sbit命令来操作存储器中的位&#xff0c;在STM32中&#xff0c;我们同样可以操作存储器中特定的位。 1、为何使用位带操作&#xff1f; 总结来说&#xff0c;一个是因为访问速度快&#xff0c;另一个是因为安全。 如果在裸机开发中&…

【学习笔记69】函数的柯里化

一、认识函数的柯里化 将一个接受多个参数的函数&#xff0c;更改为需要调用多次, 每次只传一个参数的函数利用了闭包, 延长了 外部函数的参数使用时间&#xff08;一&#xff09;基础版 function sum (a, b) {console.log(a b)}sum(10, 20);sum(10, 30);sum(10, 40); &#x…

2022卡塔尔世界杯小组赛出线形势分析指南——德国队会被西班牙做掉吗?

早点关注我&#xff0c;精彩不错过&#xff01;&#xff08;德国队出现形势分析在文末&#xff0c;可直接跳转。&#xff09;北京时间今天晚上11点整&#xff0c;如火如荼的卡塔尔世界杯就将展开小组赛第三轮比赛的争夺&#xff0c;8个小组轮番同时进行最后一轮较量&#xff0c…