【JavaEE】线程的创建及常见方法解析(Tread类)

简介: 【JavaEE】线程的创建及常见方法解析(Tread类)

1.Tread类介绍

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联,每个执行流(线程),也需要有一个对象来描述, Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度、线程管理。


2线程的构造方法——创建线程

无论使用哪一个方法创建线程,我们都需要将其中的run方法重写(run方法中写入的是线程需要执行的任务),它的作用就相当于主线程的main方法。

1.继承Thread类,重写run()方法

这个构造方法没有参数,通过创造一个类继承Thread类来实现线程线程必须重写run()方法才算完成。当我们new一个线程时,此时的线程还没开始执行,再用创建的类调用start()方法才算运行了这个线程。

class TestTread extends Thread{
    @Override
    public void run() {
        System.out.println("this tread1");
    }
}
public class testDemo2 {
    public static void main(String[] args) {
        Thread thread=new TestTread();
        thread.start();
    }
}

2.使用Runnbable接口创建线程

我们可以看出,该构造方法的参数类型是一个接口类,因此我们需要创建一个类来实现这个接口,再new一个实现Runnable的类对象,再new一个线程,将之前创建的对象放入到参数这里。

class TestTread2 implements Runnable {
    @Override
    public void run() {
        System.out.println("this t2");
    }
}
public class testDemo2 {
    public static void main(String[] args) {
        //new一个实现Runnable接口的对象
        TestTread2 testTread2=new TestTread2();
        //传入参数完成线程的创建
        Thread t2=new Thread(testTread2);
        t2.start();
    }
}

3.继承 Thread, 重写 run, 使用匿名内部类

使用匿名内部类可以省略创建对象的过程,可以减少资源消耗。

public class testDemo3 {
    public static void main(String[] args) {
        //通过Thread匿名内部类的方法创建一个线程
        Thread t1=new Thread() {
            @Override
            public void run() {
                System.out.println("this t1");
            }
        };
        t1.start();
    }
}

4.实现 Runnable, 重写 run, 使用匿名内部类

这个和3一样,可以少创建一个对象,减少资源的消耗。

public class testDemo4 {
    public static void main(String[] args) {
        Thread t1=new Thread(new  Runnable() {
            @Override
            public void run() {
                System.out.println("this t1");
            }
        });
        t1.start();
    }
}

5.使用 lambda 表达式(重点掌握)

它的实现原理和匿名内部类相似,这点不了解lambda表达式的,可以去看一下如何使用。

public class testDemo4 {
    public static void main(String[] args) 
        Thread t2=new Thread(()->{
            System.out.println("this t2");
        });
        t2.start();
    }
}


3.Tread类常见方法解读

3.1Tread类常见构造方法


1688788640989.png


3.2 Tread类的几个常见属性


1688788646458.png


ID 是线程的唯一标识,不同线程不会重复。

名称是各种调试工具用到。

状态表示线程当前所处的一个情况,下面我们会进一步说明。

优先级高的线程理论上来说更容易被调度到。

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

是否存活,即简单的理解,为 run 方法是否运行结束了。

线程的中断问题,下方会有解析

3.3启动一个线程-start()方法

调用 start 方法, 才算是在操作系统的底层创建出一个线程.

public class testDemo4 {
    public static void main(String[] args) {
        Thread t1=new Thread(new  Runnable() {
            @Override
            public void run() {
                System.out.println("this t1");
            }
        });
        t1.start();
    }
}

3.4中断一个线程


1688788704548.png


  • 使用 thread 对象的 interrupted() 方法通知线程结束
public class testDemo6 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(() ->{
            while (!Thread.interrupted()) {
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }
        });
        t1.start();
        Thread.sleep(1000*2);
        t1.interrupt();
    }
}

t1收到通知的方式有两种:


1.如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志.

当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽略这个异常, 也可以跳出循环结束线程(加break).

2.否则,只是内部的一个中断标志被设置,thread 可以通过

Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志

Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志


使用 Thread.isInterrupted() , 线程中断会清除标志位

标志位是否清除, 就类似于一个开关.

Thread.Interrupted() 相当于按下开关, 开关自动弹起来了. 这个称为 "清除标志位".

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    // //打印标准位
                    System.out.println(Thread.interrupted());
                }
        });
        thread.start();
        thread.interrupt();
    }
}

结果:



观察结果可以看出,已知interrupted()初始是true,之后就打印的值就是false,因为标志位已经被删除了。


使用 Thread.currentThread().isInterrupted() , 线程中断标记位不会清除

标志位是否清除, 就类似于一个开关.

Thread.currentThread().isInterrupted() 相当于按下开关之后, 开关弹不起来, 这个称为"不清除标志位".

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    // //打印标准位
                    System.out.println(Thread.currentThread().isInterrupted());
                }
        });
        thread.start();
        thread.interrupt();
    }
}

结果:



观察结果可以清晰的看出,打印的全是true,这是因为标志位没有被删除,它的值还是true。

3.5等待一个线程-join()


1688788828896.png


public class testDemo7 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println("第"+i+"次打印"+Thread.currentThread().getName());
            }
            System.out.println("----------");
        });
        Thread t2=new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println("第"+i+"次打印"+Thread.currentThread().getName());
            }
        });
        t1.start();
        //t2等待t1执行完毕,t2才可执行
        t1.join();
        t2.start();
    }
}

结果:



可以看出使用了join(),两个线程不再是杂乱运行了,而是先运行完t1线程,再运行的t2线程。这就是join 的作用。

3.6休眠线程

这个是比较熟悉一组方法,有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的。


1688788874612.png


3.7 实现一个简单的多线程

这两个线程任务都是打印自己的名字,其中使用的currentThread()方法是或者当前线程的引用,getName()方法就是或者线程的名字(就算我们没有给线程起名字,系统也会给它自定义一个名字)。

public class testDemo5 {
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            while (true) {
                //打印线程名称
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2=new Thread(()->{
            while (true){
                //打印线程名称
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //执行线程任务
        t1.start();
        t2.start();
    }
}

代码实现结果:



观察可以看出,两个线程是交叉运行,并且是杂乱运行,好不规律可言。没错,线程的并发就是没有规律的,谁先运行取决于操作系统如何调度线程,因此线程是"抢占式执行"。


4.线程的状态

4.1线程的六种状态

NEW: 安排了工作, 还未开始行动,刚刚创建一个Tread对象,还没开始工作。

RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作(正在CPU上执行任务或者在就绪队列中随时可以在CPU上执行的)。

BLOCKED: 这几个都表示排队等着其他事情(synchronized加锁)。

WAITING: 这几个都表示排队等着其他事情。

TIMED_WAITING: 这几个都表示排队等着其他事情(使用sleep或者join方法引起的)。

TERMINATED: 工作完成了。

4.2线程状态和状态转移



举个例子,看下图:


相关文章
|
7月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
337 0
|
4月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
257 1
|
4月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
269 1
|
11月前
|
Java 开发者
重学Java基础篇—Java类加载顺序深度解析
本文全面解析Java类的生命周期与加载顺序,涵盖从加载到卸载的七个阶段,并深入探讨初始化阶段的执行规则。通过单类、继承体系的实例分析,明确静态与实例初始化的顺序。同时,列举六种触发初始化的场景及特殊场景处理(如接口初始化)。提供类加载完整流程图与记忆口诀,助于理解复杂初始化逻辑。此外,针对空指针异常等问题提出排查方案,并给出最佳实践建议,帮助开发者优化程序设计、定位BUG及理解框架机制。最后扩展讲解类加载器层次与双亲委派机制,为深入研究奠定基础。
444 0
|
8月前
|
监控 搜索推荐 Java
Java 多线程最新实操技术与应用场景全解析:从基础到进阶
本文深入探讨了Java多线程的现代并发编程技术,涵盖Java 8+新特性,如CompletableFuture异步处理、Stream并行流操作,以及Reactive编程中的Reactor框架。通过具体代码示例,讲解了异步任务组合、并行流优化及响应式编程的核心概念(Flux与Mono)。同时对比了同步、CompletableFuture和Reactor三种实现方式的性能,并总结了最佳实践,帮助开发者构建高效、扩展性强的应用。资源地址:[点击下载](https://pan.quark.cn/s/14fcf913bae6)。
483 3
|
11月前
|
JSON 监控 网络协议
Bilibili直播信息流:连接方法与数据解析
本文详细介绍了自行实现B站直播WebSocket连接的完整流程。解析了基于WebSocket的应用层协议结构,涵盖认证包构建、心跳机制维护及数据包解析步骤,为开发者定制直播数据监控提供了完整技术方案。
1158 9
|
11月前
|
存储 监控 安全
重学Java基础篇—类的生命周期深度解析
本文全面解析了Java类的生命周期,涵盖加载、验证、准备、解析、初始化、使用及卸载七个关键阶段。通过分阶段执行机制详解(如加载阶段的触发条件与技术实现),结合方法调用机制、内存回收保护等使用阶段特性,以及卸载条件和特殊场景处理,帮助开发者深入理解JVM运作原理。同时,文章探讨了性能优化建议、典型异常处理及新一代JVM特性(如元空间与模块化系统)。总结中强调安全优先、延迟加载与动态扩展的设计思想,并提供开发建议与进阶方向,助力解决性能调优、内存泄漏排查及框架设计等问题。
486 5
|
11月前
|
安全 IDE Java
重学Java基础篇—Java Object类常用方法深度解析
Java中,Object类作为所有类的超类,提供了多个核心方法以支持对象的基本行为。其中,`toString()`用于对象的字符串表示,重写时应包含关键信息;`equals()`与`hashCode()`需成对重写,确保对象等价判断的一致性;`getClass()`用于运行时类型识别;`clone()`实现对象复制,需区分浅拷贝与深拷贝;`wait()/notify()`支持线程协作。此外,`finalize()`已过时,建议使用更安全的资源管理方式。合理运用这些方法,并遵循最佳实践,可提升代码质量与健壮性。
346 1
|
11月前
|
传感器 监控 Java
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
447 5

推荐镜像

更多
  • DNS