Java线程的基本概念和五种状态

1. 线程

1.1 创建线程

创建线程通常有以下三种方式:

实现 Runnable 接口,并重写其 run 方法:

复制代码
public` `class` `J1_Method01` `{`
    `public` `static` `void` `main(String[] args)` `{`
`        System.out.println("Main线程的ID为:"` `+ Thread.currentThread().getId());`
`        Thread thread =` `new` `Thread(new` `CustomRunner());`
`        thread.start();`
    `}`
`}`

`class` `CustomRunner` `implements` `Runnable` `{`
    `@Override`
    `public` `void` `run()` `{`
`        System.out.println("CustomRunner线程的ID为:"` `+ Thread.currentThread().getId());`
    `}`
`}

继承自 Thread 类,并重写其 run 方法:

复制代码
public` `class` `J2_Method02` `{`
    `public` `static` `void` `main(String[] args)` `{`
`        System.out.println("Main线程的ID为:"` `+ Thread.currentThread().getId());`
`        CustomThread customThread =` `new` `CustomThread();`
`        customThread.start();`
    `}`
`}`

`class` `CustomThread` `extends` `Thread` `{`
    `@Override`
    `public` `void` `run()` `{`
`        System.out.println("CustomThread线程的ID为:"` `+ Thread.currentThread().getId());`
    `}`
`}

以上两种方式都无法获取线程的返回值,如果想要获取线程的返回值,需要实现 Callable 接口:

复制代码
public` `class` `J3_Method03` `{`
    `public` `static` `void` `main(String[] args) throws ExecutionException, InterruptedException {`
`        Task task =` `new` `Task();`
`        FutureTask<Integer> futureTask =` `new` `FutureTask<>(task);`
        `new` `Thread(futureTask).start();`
`        System.out.println("获得线程返回值:"` `+ futureTask.get());`
    `}`
`}`

`class` `Task` `implements` `Callable<Integer>` `{`
    `@Override`
    `public Integer call()` `{`
        `return` `100;`
    `}`
`}`
`// 输出:`
`获得线程返回值:100

1.2 线程属性

编号 (ID) :用于标识线程的唯一编号,只读属性。

名称 (Name):用于定义线程名称,可读可写。

线程类别 (Daemon):通过线程的 `setDaemon(boolean on)` 方法进行设置,为 true 表示设置为守护线程,否则为用户线程。用户线程会阻止 Java 虚拟机正常停止,守护线程则不会。通常可以把一些不重要的线程设置为守护线程,比如监控其他线程状态的监控线程,当其他工作线程停止后,虚拟机就可以正常退出。

优先级 (Priority):Java 线程支持 1 到 10 十个优先级,默认值为 5 。Java 线程的优先级本质上只是给线程调度器一个提示信息,它并不能保证线程一定按照优先级的高低顺序运行,所以它是不可靠的,需要谨慎使用。在 Java 平台中,子线程的优先级默认与其父线程相同。

1.3 线程状态

Java 线程的生命周期分为以下五类状态:

RUNABLE:该状态包括两个子状态:READY 和 RUNING 。处于 READY 状态的线程被称为活跃线程,被线程调度器选中后才开始运行,转化为 RUNING 状态。

BLOCKED:一个线程发起一个阻塞式 IO 操作后(如文件读写或者阻塞式 Socket 读写),或者申请一个由其他线程持有的独占资源(比如锁)时,相应的线程就会处于该状态。

WAITING:线程处于无时间限制的等待状态。

TIMED_WAITING:有时间限制的等待状态,如果在指定时间内并没有执行的特定的操作,则该线程自动转换为 RUNABLE。

TERMINATED:`Thread.run()`正常返回或者由于抛出异常而提前终止,则对应的线程都会处于该终止状态。

各个状态之间的转换关系如下图:

1.4 线程终止

通常线程会随着代码的运行完成而终止,但如果你在线程中进行了死循环操作,此时就需要考虑如何安全地停止线程?虽然 Thread 类提供了 `stop()` 方法,但其已经被标识为废弃,因为 `stop()` 只是暴力的停止线程, 但此时线程中的操作仍可能处于中间状态,此时暴力地停止就可能会产生非预期的结果。想要安全的停止线程,可以通过改变终止标志位的方式来实现:

复制代码
public` `class` `ThreadStop` `{`

    `private` `static volatile boolean stopFlag =` `true;`

    `public` `static` `void` `main(String[] args) throws InterruptedException {`
`        Thread thread =` `new` `Thread(()` `->` `{`
            `while` `(stopFlag)` `{`
                `try` `{`
`                    Thread.sleep(100);`
`                    System.out.println("持续输出");`
                `}` `catch` `(InterruptedException e)` `{`
`                    e.printStackTrace();`
                `}`
            `}`
        `});`
`        thread.start();`
`        Thread.sleep(3` `*` `1000);`
`        stopFlag =` `false;`
`        System.out.println("线程终止");`
    `}`
`}

1.5 线程中断

除了终止线程外,JDK 中提供了以下方法用于实现线程中断:

Thread.interrupt() :用于给目标线程设置中断标志位,但实际上并不能中断线程;

Thread.isInterrupted():通过检查中断标志位,判断当前线程是否被中断;

Thread.interrupted():用来判断当前线程的中断状态,同时会清除当前线程的中断标志位。

线程中断与线程终止的区别在于:线程中断只是告诉目标线程,我希望你停止运行,即设置标志位,而线程是否真的停止则是由其自行决定。示例如下:

复制代码
/** `
` * interrupt() 只是设置中断标志位,并不能中断线程,所以子线程会持续打印`
` */`
`public` `class` `J1_Interrupt` `{`
    `public` `static` `void` `main(String[] args) throws InterruptedException {`
`        Thread thread =` `new` `Thread(()` `->` `{`
            `while` `(true)` `{`
`                System.out.println("子线程打印");`
            `}`
        `});`
`        thread.start();`
`        Thread.sleep(10);`
`        thread.interrupt();`
    `}`
`}`


`/**`
` * isInterrupted() 用于检查当前线程是否存在中断标志位,配合interrupt()使用可以中断线程`
` */`
`public` `class` `J2_IsInterrupted` `{`
    `public` `static` `void` `main(String[] args) throws InterruptedException {`
`        Thread thread =` `new` `Thread(()` `->` `{`
            `while` `(!Thread.currentThread().isInterrupted())` `{`
`                System.out.println("子线程打印");`
            `}`
        `});`
`        thread.start();`
`        Thread.sleep(10);`
`        thread.interrupt();`
    `}`
`}`

`/**`
` * interrupted() 用于判断当前线程的中断状态,并清除当前线程的中断标志位`
` */`
`public` `class` `J3_Interrupted` `{`

    `public` `static` `void` `main(String[] args) throws InterruptedException {`
`        Thread thread =` `new` `Thread(()` `->` `{`
            `// 此时由于标志位被清除,此时子线程依然会持续打印`
            `while` `(!Thread.interrupted()` `||` `!Thread.currentThread().isInterrupted())` `{`
`                System.out.println("子线程打印");`
            `}`
        `});`
`        thread.start();`
`        Thread.sleep(10);`
`        thread.interrupt();`
    `}`
`}`
`

2. 基本概念

2.1 变量分类

状态变量 (State Variable) :即类的实例变量,非共享的静态变量。

共享变量 (Shared Variable) : 即可以被多个线程共同访问的变量。

2.2 竞态

如果多个线程并发的操作共享变量,并且这些操作不全是只读操作,那么它们彼此之间就会存在竞争,这种状态就称为竞态。由于竞态的存在,此时可能存在一个类在单线程的环境下能够正常运转,但在多线程的环境下却运行出错,此时就称这个类是线程不安全的。

复制代码
public` `class` `J1_ThreadUnsafe` `{`

    `private` `static int i =` `0;`

    `public` `static` `void` `main(String[] args) throws InterruptedException {`
`        IncreaseTask task =` `new` `IncreaseTask();`
`        Thread thread1 =` `new` `Thread(task);`
`        Thread thread2 =` `new` `Thread(task);`
`        thread1.start();`
`        thread2.start();`
        `// 等待线程结束再打印返回值`
`        thread1.join();`
`        thread2.join();`
`        System.out.println(i);` `// 线程不安全,输出总是小于:200000`
    `}`

    `static` `class` `IncreaseTask` `implements` `Runnable` `{`
        `@Override`
        `public` `void` `run()` `{`
            `for` `(int j =` `0; j <` `100000; j++)` `{`
                `inc();`
            `}`
        `}`

        `private` `void` `inc()` `{`
`            i++;`
        `}`
    `}`
`}
相关推荐
海兰18 分钟前
使用 Spring AI 打造企业级 RAG 知识库第二部分:AI 实战
java·人工智能·spring
历程里程碑35 分钟前
二叉树---二叉树的中序遍历
java·大数据·开发语言·elasticsearch·链表·搜索引擎·lua
小信丶1 小时前
Spring Cloud Stream EnableBinding注解详解:定义、应用场景与示例代码
java·spring boot·后端·spring
无限进步_1 小时前
【C++】验证回文字符串:高效算法详解与优化
java·开发语言·c++·git·算法·github·visual studio
亚历克斯神1 小时前
Spring Cloud 2026 架构演进
java·spring·微服务
七夜zippoe1 小时前
Spring Cloud与Dubbo架构哲学对决
java·spring cloud·架构·dubbo·配置中心
海派程序猿1 小时前
Spring Cloud Config拉取配置过慢导致服务启动延迟的优化技巧
java
阿维的博客日记1 小时前
为什么不逃逸代表不需要锁,JIT会直接删掉锁
java
William Dawson1 小时前
CAS的底层实现
java
九英里路1 小时前
cpp容器——string模拟实现
java·前端·数据结构·c++·算法·容器·字符串