JavaEE|多线程(二)

线程的命名

可以给线程起名(中文名也是可以的),例如对三个线程起不同名字再去jconsole上观察

java 复制代码
public class Demo06 {
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            while(true){
                System.out.println("hello t1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t2=new Thread(()->{
            while(true){
                System.out.println("hello t2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t3=new Thread(()->{
            while(true){
                System.out.println("hello t3");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    

当不给线程起名的时候,线程会自动命名为Thread-0,Thread-1....

通过名字描述线程是干啥的,方便调试

主线程

main方法执行完毕,主线程就已经结束了

以前我们对于main的认识是当main方法执行完之后,程序就已经结束了(进程);但实际上,以前的

认知是针对于单线程程序

Thread 类及常见方法

Thread 类是JVM用来管理线程的一个类,换句话说,每个线程都有一个唯一的Thread 对象与之关 联。

Thread 的常见构造方法

|-----------------------------------------------|----------------------------|
| 方法 | 声明 |
| Thread() | 创建线程对象 |
| Thread(Runnable target) | 使用Runnable对象创建线程对象 |
| Thread(String name) | 创建线程对象,并且命名 |
| Thread(Runnable target,String name) | 使用Runnable对象创建线程对象,并命名 |
| 【了解】Thread(ThreadGroup group,Runnable target) | 线程可以被用来分组管理,分好的组即为线程组,了解即可 |

Thread 的几个常见属性

|--------|-----------------|
| 属性 | 获取方法 |
| ID | getId() |
| 名称 | getName() |
| 状态 | getState() |
| 优先级 | getPriority() |
| 是否后台线程 | isDaemon() |
| 是否存活 | isAlive() |
| 是否被中断 | isInterrupted() |

ID 是线程的D唯一标识,不同线程不会重复,标识线程身份的效果,类似于PID

Daemon

关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。

例如,当main线程虽然结束了,但是t1,t2.t3还在,所以进程仍然存在,t1,t2,t3这样的线程就称为

"前台线程"

黑框部分是JVM自带的线程,这些线程不影响进程的结束,如果进程要结束了,即使这些线程继续

运行,也会随之结束,这些就是后台线程

我们创建的代码包括main线程都默认是前台线程,但可以通过setDaemon方法来进行修改

java 复制代码
public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
           while (true){
               System.out.println("hello thread");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
        });
        t.setDaemon(true);
        t.start();
        for (int i = 0; i < 2; i++) {
            System.out.println("hello main");
            Thread.sleep(1000);
        }
        System.out.println("main结束");
    }

把t设为后台线程,无力阻止main线程的结束,这样的设置得在start之前运行

isAlive()

创建Thread对象和系统中的线程关系是一一对应的,但是Thread对象的生命周期和系统中的线程的生命周期是不同的(可能存在,Thread对象存活,但是系统中的线程已经销毁的情况)

java 复制代码
  public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            for (int i = 0; i < 3; i++) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        System.out.println(t.isAlive());
        t.start();
        while (true){
            System.out.println(t.isAlive());
            Thread.sleep(1000);
        }

当for循环结束之后,线程的入口方法的逻辑结束了,系统中对应的线程就随之销毁了

但是对应的t对象会仍然存在

启动线程-start()

java标注库/JVM提供的方法,本质上是调用操作系统的API

每个Thread对象,都是只能start一次的

每次创建一个新的线程,都得创建一个新的Thread对象(不能重复利用)

start和run的区别

run是线程的入口方法,不需要手动调用

start是调用系统api

二者本质上没有很大的关系

中断线程

1.通过共享的标记来终止

java 复制代码
  private static  boolean isFinished=false;
    public static void main(String[] args) throws InterruptedException {

        Thread t=new Thread(()->{
            while (!isFinished){
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        isFinished=true;
    }

通过定应全程变量来控制线程进度

但当isFinished变为局部变量时,此时无法对其进行修改

通过观察报错原因我们可以知道,这个isFinished应该为常量

这与我们前面的isFinished修改相互矛盾,这其实是因为lambda里面希望使用外面的函数,触发了

变量捕获的语法

lambda是回调函数,执行时机是很久之后(操作系统真正创建线程之后,才会执行),可能会出

现后续线程创建好了,但是当前main这里的方法都执行完了,对应的isFinished就销毁了

Java为解决上述的问题把捕获的变量拷贝一份在lambda里面,外面的变量是否销毁都不会影响

lambda里面的执行

这里的拷贝意味着这样的变量就不再适合修改(修改的本质是修改一方,另一方不会随之变化)

让isFinished变为静态变量时,isFinished在类加载时就会存在,而不是通过new时才会存在

而对于引用类型,引用本身是不能修改(不能修改这个引用指向的其他对象),但是引用指向的对象

本体是可以修改的

把上述的代码改成员变量此时不再是"变量捕获"语法,而是切换成"内部类访问外部类的成员"语法

lambda本质上是函数式接口,相当于一个内部类,isFinished就是外部类的成员,内部类就是能够

访问到外部类的成员,成员变量生命周期也是让GC来管理的,在lambda里面不担心变量生命周期

失效的问题,也就不必拷贝,也就不必限制final之类的

调用 interrupt() 方法

Java的Thread对象中提供了现成的变量,直接进行判定,不需要自己创建了

lambda这里的定义是在new Thread之前,也是在Thread t声明之前,所以Thread创建的对象也无

法在lambda表达式中使用

Thread提供了访问当前对象的静态方法currentThread(),获取到的就是那个线程的Thread引用

java 复制代码
public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            while (!Thread.currentThread().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);
        System.out.println("main尝试终止t线程");
        t.interrupt();
        System.out.println(Thread.currentThread().getName());
    }

判定Thread里的boolean变量的值,判定线程是否终止了

主动去进行终止,修改这个boolean变量的值,除了设置boolean变量(标志位)之外,还能够唤醒

sleep这样的阻塞方法

这个线程掀桌了,可以通过修改抛出异常的部分来控制终止

加上break是立即终止

啥都不写就是不终止

catch中执行一些逻辑在break,就是稍后终止

相关推荐
tianyu2346 分钟前
Java 正则表达式终极指南:从 Pattern 到 Matcher,从双重转义到性能优化,一篇全搞定
java·开发语言·正则表达式·group·find·pattern·matcher
caimouse2 小时前
ReactOS 窗口系统分析(23):键盘 — keyboard.c + kbdlayout.c
c语言·开发语言·计算机外设
Emily156853598702 小时前
Emerson 1C31129G03 Analog Input Module
java·开发语言·前端·plc·emerson·1c31129g03·input module
就叫_这个吧7 小时前
Java递归方法实现面包屑导航
java·开发语言
fīɡЙtīиɡ ℡7 小时前
AI 应用系统设计
java·开发语言·人工智能
城管不管7 小时前
重生——第十一次面试之挖财一面2026.8.19已OC
java·服务器·jvm·数据库·spring·面试·职场和发展
码匠许师傅7 小时前
【C++ 面试真题】26. 聊聊 C++ 的智能指针
java·c++·面试
AI绘画哇哒哒8 小时前
【建议收藏!】35岁后端血泪忠告,这3类人别硬转Agent(过来人亲述)
java·人工智能·后端·ai·程序员·大模型·agent
最强小杰8 小时前
gpt-5.6-sol 频繁报 503 怎么办?区分容量熔断和限速 429 的排查方法 + 可复用 retry wrapper
java·人工智能·gpt·ai
今天AI了吗9 小时前
Python 基础语法(一):常量、变量、输入输出与运算符
开发语言·数据库·人工智能·python·sql·深度学习·机器学习