java中操作线程

文章目录

  • 前言
    • 创建与运行线程
    • 1. 创建线程
      • ①、方法1(直接new)
      • ②、方法2(使用Runnable配合Thread进行new操作)
      • ③、方法3(FutureTask对象实现)
      • ④、线程创建原理
      • 特别注意!
    • 2查看与杀死线程
      • ①、 Windows下 :
      • ②、 Java下 :
    • 3.线程状态
      • 2.7.1 五种状态(操作系统层面)
      • 2.7.2 六种状态(java层面)
    • 4. 守护线程
    • 5.常用方法
      • ①、 start && run方法详解
      • ②、 sleep与yield方法
      • ③、 线程优先级
      • ④、 yield和线程优先级总结
      • ⑤、 sleep应用
      • ⑥、 join方法
        • 为什么需要join?
        • join应用之同步
        • join应用之限时同步
      • ⑦、 interrupt方法
        • 打断阻塞(sleep、wait、join)的线程
        • 打断正常的线程
        • 打断park线程
        • interrupt和isInterrupted

前言


今天主要总结一些常用的方法、状态,以及方法使用细节,为后期原理的解析做铺垫。


创建与运行线程

1. 创建线程

①、方法1(直接new)

public static void main(String[] args) {
    //创建线程
    Thread t1 = new Thread() {
        @Override
        public void run() {
            System.out.println("我创建了");
        }
    };
    //运行线程
    t1.start();
}

运行结果:

在这里插入图片描述

两种命名方式:

// 命名方式1
Thread t1 = new Thread("t1") {
    @Override
    public void run() {
        log.debug("running");
    }
};

//命名方式2
t1.setName("t1");

②、方法2(使用Runnable配合Thread进行new操作)

public static void main(String[] args) {
        //创建线程
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                log.debug("running");
            }
        };

        Thread t2 = new Thread(runnable, "t2");

        //运行线程
        t2.start();

        log.debug("running");
    }

lambda简化写法:

//创建线程
Runnable runnable = () -> {
        log.debug("running");
};

运行结果:

在这里插入图片描述

  • ③、Thread与Runnable关系

两种方法中更推荐第二种方法,

  1. 使用Runnable方便与线程池等高级api配合。
  2. 因为使用Runnable可以将任务与线程分离开来,使得编程更加灵活。
  3. 而第一种方法则是将任务与线程合并在了一起,耦合度高。

③、方法3(FutureTask对象实现)

public static void main(String[] args) throws ExecutionException, InterruptedException {
    //创建线程
    FutureTask<Integer> task = new FutureTask(new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            log.debug("running...");
            Thread.sleep(2000);
            return 100;
        }
    });

    Thread t3 = new Thread(task, "t3");
    //运行线程
    t3.start();
    log.debug("{}", task.get());

}

④、线程创建原理

特别注意!

​ 不管创建方法有多少个,只有 new Thread().start() 才是真正的创建并运行了一个线程,其它的,比如通过在内部类重写run()方法或者其它代码实现方式,其本质不会创建线程,最终都要通过new Thread()来创建。

2查看与杀死线程

①、 Windows下 :

  • 任务管理器查看
  • taskkill --------- 杀死进程
  • tasklist --------- 查看进程

在这里插入图片描述

②、 Java下 :

  • jps查看Java所有进程
  • jstack 来查看某个java进程的所有状态
  • jconsole来查看某个java进程中线程运行情况(图形化界面)

jconsole界面
在这里插入图片描述

3.线程状态

  • 线程状态有好几种说法,有说5种,有说6种的, 我们都来看看

2.7.1 五种状态(操作系统层面)

在这里插入图片描述

  • 【终止状态】 表示线程已经执行完毕,生命周期已经结束,不会转换为其它状态。

2.7.2 六种状态(java层面)

在这里插入图片描述

  • NEW : 线程刚被创建,但是未曾调用start()方法
  • RUNNABLE : 当调用了start()方法之后,需要注意,Java API 层面的RUNNABLE转台涵盖了 操作系统

层面的【可运行状态】、【运行状态】、【阻塞状态】(由BIO造成的线程阻塞,再java中无法区分,仍然认为是可运行)

  • BLOCKED、WAITING、TIMED_WAITING为Java层面对于【阻塞状态】的细分。

  • TERMIANTED : 线程代码运行结束

示例代码:

public static void main(String[] args) throws IOException {
    Thread t1 = new Thread("t1") {
        @Override
        public void run() {
            log.debug("running...");
        }
    };

    Thread t2 = new Thread("t2") {
        @Override
        public void run() {
            while(true) { // runnable

            }
        }
    };
    t2.start();

    Thread t3 = new Thread("t3") {
        @Override
        public void run() {
            log.debug("running...");
        }
    };
    t3.start();

    Thread t4 = new Thread("t4") {
        @Override
        public void run() {
            synchronized (TestState.class) {
                try {
                    Thread.sleep(1000000); // timed_waiting
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };
    t4.start();

    Thread t5 = new Thread("t5") {
        @Override
        public void run() {
            try {
                t2.join(); // waiting
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
    t5.start();

    Thread t6 = new Thread("t6") {
        @Override
        public void run() {
            synchronized (TestState.class) { // blocked
                try {
                    Thread.sleep(1000000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };
    t6.start();

    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    log.debug("t1 state {}", t1.getState());
    log.debug("t2 state {}", t2.getState());
    log.debug("t3 state {}", t3.getState());
    log.debug("t4 state {}", t4.getState());
    log.debug("t5 state {}", t5.getState());
    log.debug("t6 state {}", t6.getState());
    System.in.read();
}

运行结果:

在这里插入图片描述

4. 守护线程

  • 守护线程:当其它非守护线程执行结束了,即使守护线程也没有执行完,也会强制结束。

在这里插入图片描述

​ 如图所示,当我们设定守护进程之后,主线程结束之后,t1就会自动结束了。

​ 但如果不设置的话,程序就会一直运行,无法结束。

设置守护线程的方法是t1.**setDaemon(true)**方法。

5.常用方法

①、 start && run方法详解

  • 区别对比

我们直接在代码中查看:

@Slf4j(topic = "c.Test5")
public class Test5 {
    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            log.debug("running...");

        }, "t1");

        t1.run();
        t1.start();
    }

}

结果:

在这里插入图片描述

从运行结果可以看出,直接调用run()方法,其本质还是在main线程中运行。

调用start()的时候,才是新开了一个线程。

  • 运行前后状态对比

代码:

@Slf4j(topic = "c.Test5")
public class Test5 {
    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            log.debug("running...");

        }, "t1");

        System.out.println(t1.getState());
        t1.start();
        System.out.println(t1.getState());
    }

}

运行结果:

在这里插入图片描述

从运行结果可以看出,在start()运行前,线程处于NEW的状态,运行之后,是RUNNABLE状态,即可执行状态。

  • 注意

在RUNNABLE状态下,就不可以再次使用start()方法,也就是一个线程不能重复启动两次。

②、 sleep与yield方法

  • sleep()方法
    • 状态

代码:

public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread("t1") {
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    };

    t1.start();
    Thread.sleep(500);
    log.debug("t1 state:{}", t1.getState());
}

运行结果:

在这里插入图片描述

此处是主线程先稍微停顿500ms,此时t1线程已经处于sleep状态了,然后在主线程打印t1的状态就是TIMED_WAITING。

    • 打断

代码:

public static void main(String[] args) {
    Thread t1 = new Thread("t1") {
        @Override
        public void run() {
            log.debug("enter sleep...");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                log.debug("wake up...");
                throw new RuntimeException(e);
            }
        }
    };

    t1.start();

    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

    log.debug("interrupt...");
    t1.interrupt();

}

结果:

在这里插入图片描述

当线程睡眠中,被唤醒之后,就会抛出异常。

    • 可读性

代码:

TimeUnit.SECONDS.sleep(1); // 等价于Thread.sleep(1000)
  • yield方法
  1. 调用yield会让当前线程从Running进入Runnable就绪状态,然后调度执行其它线程
  2. 具体的实现依赖于操作系统的任务调度器
  • yield&&sleep的区别
  1. yield执行后处于Runnable状态,还是有执行的机会的,sleep执行处于TIMED_WAITING,sleep下会有一个等待时间,而yield则是将cpu的时间片重新分配,几乎没有等待时间。
  • yield作用

代码:

@Slf4j(topic = "c.Test9")
public class Test9 {
    public static void main(String[] args) {
        Runnable task1 = () -> {
            int count = 0;
            for(;;) {
                System.out.println("------>1" + count ++);
            }
        };
        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.start();
        t2.start();
    }
}

不执行yield的时候,结果:

在这里插入图片描述

执行yield的时候,结果:

在这里插入图片描述

以上都是死循环中截取了有交界的地方。我们可以看出,不执行yield的时候,两者的运行次数大致相同。但是,在给task2添加了yield方法暂停之后,任务1执行次数显著高于任务2,但是任务2也并不是不执行,故能验证yield的作用。

换句话说,执行yield相当于让它先抽身出来,没有挡后面人的道,sleep相当于挡在那里,不让其他人过。执行yield的话,其他线程就没有等待时间。

③、 线程优先级

  • 线程优先级会提示,调度器有限调度该线程,但是其仅仅是一个提示,调度器几乎可以忽略它。
  • 如果cpu比较忙,那么优先级高的线程会有更多的时间片,但是cpu闲的时候,优先级几乎没有用。

代码:

Runnable task1 = () -> {
    int count = 0;
    for(;;) {
        System.out.println("------>1 " + count ++);
    }
};
Runnable task2 = () -> {
    int count = 0;
    for(;;) {
        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();

结果:

在这里插入图片描述

从结果可以看出,不忙的时候,优先级几乎没有影响。

④、 yield和线程优先级总结

  • 不管是yield还是线程优先级,两者对程序产生的影响,都要取决于操作系统的任务调度器,与sleep有本质不同。

⑤、 sleep应用

  • 在一个while(true)的程序中,里面必须加一个Thread.sleep(50)的方法,为了防止无意义的while循环中占用资源(防止cpu占用100%),加了sleep可以将cpu控制权让出来。
while(true) {
    try {
        Thread.sleep(50);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}

循环会占用大量 CPU 资源,导致程序的性能下降。通过 Thread.sleep(50),你让线程每次执行后等待 50 毫秒,从而减少 CPU 的负担。

⑥、 join方法

  • join作用 : 等待线程结束
为什么需要join?

如图情况

static int r = 1;

public static void main(String[] args) {
    test01();
}
public static void test01() {
    log.debug("start");
    Thread t1 = new Thread("t1") {
        @Override
        public void run() {
            log.debug("start");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.debug("end");
            r = 10;
        }
    };
    t1.start();
    log.debug("结果为:{}", r);
    log.debug("end");
}

运行结果:

在这里插入图片描述

​ 我们希望r = 10之后再执行主线程的打印操作,即等待t1线程执行完再执行主线程任务。此时我们用sleep和yield是不是都不好操作?所以,我们引入了join方法,就是为了应对,存在需要等待线程执行结束的情况。

修正代码 :

t1.start();
try {
    t1.join();
} catch (InterruptedException e) {
    throw new RuntimeException(e);
}

运行结果 :

在这里插入图片描述

join应用之同步

以调用方角度来讲:


  • 需要等待结果返回,才能继续运行就是同步调用。
  • 不需要等待结果返回,就能继续运行就是异步调用。

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

结果:

在这里插入图片描述

可以看到,结果中总共花了两秒的时间,在执行t1.join()前,t1线程就已经执行完了。

因此总花费时间就是两秒。

参考流程图:

在这里插入图片描述

join应用之限时同步

代码:

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

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

// 线程执行结束会导致 join 结束
log.debug("join begin");
t1.join(3000);
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);

结果:

在这里插入图片描述

可以看出,此时运行时间以实际运行时间为准。

虽然调用方法的时候,限定三秒,但是实际上两秒就执行完了,所以最终等待时间以最终执行时间为准。

⑦、 interrupt方法

打断阻塞(sleep、wait、join)的线程
  • 打断sleep线程,会清空打断状态。

代码:

Thread t1 = new Thread(() -> {
    log.debug("sleep....");
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

}, "t1");

t1.start();
Thread.sleep(1000);
log.debug("interrupt...");
t1.interrupt();
Thread.sleep(1000);
log.debug("打断标记:{}", t1.isInterrupted());

运行结果

在这里插入图片描述

打断正常的线程

代码

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();

结果:

在这里插入图片描述

从结果可以看出,interrupt() 可以用来较为优雅的打断线程,同时不影响程序正常运行。

打断park线程
  • 打断park线程,不会清空线程状态
  • LockSupport.park();和sleep()方法效果一样,都会让线程阻塞。
  • 不同的是,park被打断后,不会被清空线程状态。
public static void main(String[] args) 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();
}

运行结果:

在这里插入图片描述

interrupt和isInterrupted

在这里插入图片描述

在这里插入图片描述

后者为静态方法,了解即可。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【redis】redix在Linux下的环境配置和redis的全局命令

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如…

百度智能云千帆AppBuilder升级,百度AI搜索组件上线,RAG支持无限容量向量存储!

百度智能云千帆 AppBuilder 发版升级&#xff01; 进一步降低开发门槛&#xff0c;落地大模型到应用的最后一公里。在千帆 AppBuilder 最新升级的 V1.1版本中&#xff0c;企业级 RAG 和 Agent 能力再度提升&#xff0c;同时组件生态与应用集成分发更加优化。 • 企业级 RAG&am…

uniappp配置导航栏自定义按钮(解决首次加载图标失败问题)

1.引入iconfont的图标&#xff0c;只保留这两个文件 2.App.vue引入到全局中 import "./static/fonts/iconfont.css"3.pages.json中配置text为图标对应的unicode {"path": "pages/invite/invite","style": {"h5": {"…

融云分享基于 Rust 的鸿蒙 SDK 开发实践

12 月 5 日&#xff0c;以“同心聚力&#xff0c;共建共享鸿蒙新生态”为主题的“鸿蒙生态伙伴 SDK 开发者论坛”在京举行。 融云凭借对鸿蒙生态的率先适配和创新贡献&#xff0c;荣获华为鸿蒙生态“HarmonyOS NEXT SDK 星河奖”。 本次论坛邀请了多位行业领导者参与&#xff…

iperf3 带宽性能测量工具

随笔记录 目录 1 背景介绍 2. Iperf3 2.1 基本框架介绍 2.2 Iperf3 发送TCP包 2.3 IPerf 发送UDP 包 1 背景介绍 基于测试USER DMA 压力测试需求。 2. Iperf3 2.1 基本框架介绍 Iperf3 发送数据包 TCP/UDP 包 1. 查看网卡配置信息 2. 此处因共用一张板卡&#xff0…

Java-27 深入浅出 Spring - 实现简易Ioc-03 在上节的业务下手动实现IoC

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…

vue使用pdfh5.js插件,显示pdf文件白屏

pdfh5&#xff0c;展示文件白屏&#xff0c;无报错 实现效果图解决方法(降版本)排查问题过程发现问题查找问题根源1、代码写错了&#xff1f;2、预览文件流的问题&#xff1f;3、pdfh5插件更新了&#xff0c;我的依赖包没更新&#xff1f;4、真相大白 彩蛋 实现效果图 解决方法…

【机器学习算法】——决策树之集成学习:Bagging、Adaboost、Xgboost、RandomForest、XGBoost

集成学习 **集成学习(Ensemble learning)**是机器学习中近年来的一大热门领域。其中的集成方法是用多种学习方法的组合来获取比原方法更优的结果。 使用于组合的算法是弱学习算法&#xff0c;即分类正确率仅比随机猜测略高的学习算法&#xff0c;但是组合之后的效果仍可能高于…

C/S软件授权注册系统(Winform+WebApi+.NET8+EFCore版)

适用软件&#xff1a;C/S系统、Winform桌面应用软件。 运行平台&#xff1a;Windows .NETCore&#xff0c;.NET8 开发工具&#xff1a;Visual Studio 2022&#xff0c;C#语言 数据库&#xff1a;Microsoft SQLServer 2012&#xff0c;Oracle 21c&#xff0c;MySQL8&#xf…

Big Model weekly | 第49期

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 01 Magnetic Preference Optimization: Achieving Last-iterate Convergence for Language Models Alignment 自我对弈方法在多个领域增强模型能力方面展现出了显著的成功。在基于人类反馈的强化学习&#xff0…

如何建设金融数据中心

目录 总则 概述 要求 基本原则 数据中心治理 概述 战略管控 战略规划 战略实施 延伸阅读 总则 概述 本文以描述金融数据中心的治理域内容为基础,从金融数据中心建设、运营及安全保障的角度出 发,逐一描述场地环境、网络通信、运行管理和风险管控等能力域的具体…

医学分割数据集B超图片肝脏分割数据集labelme格式271张1类别

数据集格式&#xff1a;labelme格式(不包含mask文件&#xff0c;仅仅包含jpg图片和对应的json文件) 图片数量(jpg文件个数)&#xff1a;271 标注数量(json文件个数)&#xff1a;271 标注类别数&#xff1a;1 标注类别名称:["liver"] 每个类别标注的框数&#xf…

Android:展锐T760平台camera PDAF调试

一、平台PDAF流程 目前展锐平台主要支持Shield PD Sensor、Dual PD Sensor 1、Shield PD Sensor Type1相位差和信心度结果直接从Sensor输出,不经过平台算法库。 Type2Sensor端抽取PD信息, 放在一块buffer输出, PDAF算法库算出相位差和信心度。 Type3Sensor端直接输出将带有…

MySQL的历史和地位

秋招之后&#xff0c;开始深入学习后端开发知识啦。把学到的东西分享给大家最开心啦。就从MySQL开始吧。 首先说一下MySQL的历史和地位。主要是看一下我们为什么要学习&#xff0c;而不是说让我们学什么我们就学什么。 地位 这张图是我从DB-Engines截取的2024年12月最新的数据…

鸿蒙项目云捐助第七讲鸿蒙App应用的首页推荐模块布局的实现

鸿蒙项目云捐助第七讲鸿蒙App应用的首页推荐模块布局的实现 最后设置首页的推荐模块&#xff0c;参考模板如下图所示。 一、首页热门推荐模块的实现 对于热门推荐模块&#xff0c;先有上面的小标题栏&#xff0c;这里的标题栏也有一个小图标&#xff0c;首先从“百度图库”中…

使用ENSP实现NAT(2)

一、NAT的类型 二、静态NAT 1.项目拓扑 2.项目实现 路由器AR1配置&#xff1a; 进入系统视图 sys将路由器命名为AR1 sysname AR1关闭信息中心 undo info-center enable 进入g0/0/0接口 int g0/0/0将g0/0/0接口IP地址配置为192.168.10.254/24 ip address 192.168.10.254 24进…

【1211更新】腾讯混元Hunyuan3D-1文/图生3D模型云端镜像一键运行

目录 项目介绍 显存占用 11月21 新增纹理烘焙模块Dust3R 烘焙相关参数&#xff1a; AutoDL云端镜像 启动说明 标准模型下载 【1212更新】腾讯混元Hunyuan3D-1文图生3D模型云端镜像一键运行 项目介绍 https://github.com/Tencent/Hunyuan3D-1 腾讯混元 3D 生成模型,支持…

PHP 应用 ImageMagick

ImageMagick是什么&#xff1f; ImageMagick是一款功能强大、跨平台的图像处理软件。它可以读取、转换和输出大量的图片格式&#xff0c;支持各种各样的图像处理操作&#xff0c;如调整大小、旋转、加水印、格式转换等。在PHP中使用ImageMagick&#xff0c;可以让我们在web应用…

OpenCV圆形标定板检测算法findGrid原理详解

OpenCV的findGrid函数检测圆形标定板的流程如下: class CirclesGridClusterFinder {CirclesGridClusterFinder(const CirclesGridClusterFinder&); public:CirclesGridClusterFinder

阿里云服务器手动搭建WordPress【官方文档注意事项】

这是官方文档 注意事项 先配LNMP&#xff0c;我的上一篇文章到这里发现&#xff0c;没有基于ubuntu的教程&#xff0c;所以创建服务器时选择centos在官方文档第四步“下载WordPress&#xff0c;并移动至网站根目录”中&#xff0c;首先它让cd /usr/share/nginx/html&#xff…