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++;`
        `}`
    `}`
`}
相关推荐
Lojarro13 分钟前
【Spring】Spring框架之-AOP
java·mysql·spring
莫名其妙小饼干16 分钟前
网上球鞋竞拍系统|Java|SSM|VUE| 前后端分离
java·开发语言·maven·mssql
isolusion28 分钟前
Springboot的创建方式
java·spring boot·后端
zjw_rp1 小时前
Spring-AOP
java·后端·spring·spring-aop
Oneforlove_twoforjob1 小时前
【Java基础面试题033】Java泛型的作用是什么?
java·开发语言
TodoCoder1 小时前
【编程思想】CopyOnWrite是如何解决高并发场景中的读写瓶颈?
java·后端·面试
向宇it1 小时前
【从零开始入门unity游戏开发之——C#篇24】C#面向对象继承——万物之父(object)、装箱和拆箱、sealed 密封类
java·开发语言·unity·c#·游戏引擎
小蜗牛慢慢爬行1 小时前
Hibernate、JPA、Spring DATA JPA、Hibernate 代理和架构
java·架构·hibernate
星河梦瑾2 小时前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全
黄名富2 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua