Java线程——从创建第一个线程到休眠线程

目录

一、引言

二、Thread类

三、中断线程

[3.1 interrupt():发送中断信号](#3.1 interrupt():发送中断信号)

[3.2 isInterrupted():判断线程是否被中断](#3.2 isInterrupted():判断线程是否被中断)

[3.3 interrupted():判断并清除中断状态](#3.3 interrupted():判断并清除中断状态)

四、等待线程

五、线程的休眠

六、总结


一、引言

这一期来简单的说一下,关于Java当中如何去创建线程并且去启动,这一期还会说到等待线程,休眠线程等一些基础的内容,带你直接吃透,那么话不多说直接进入这一期的内容吧。

二、Thread类

Java为我们提供Thread类供我们去操作线程,要使用Thread去创建线程我们这里介绍4种,在那之前先介绍两种最常见的方法分别是run()方法和start()方法,run方法是负责线程要执行的内容也就是对run方法进行重写,写出要执行的内容,start就是负责启动线程了。下面先用代码来介绍第一种方法--直接创建一个类去继承Thread类然后重写run方法启动

java 复制代码
class MyThread extends Thread {
    public void run() {
        while (true) {
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

public class Test {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        //在系统里创建了一个线程
        thread.start();
        while (true) {
            System.out.println("hello Man");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

这里可能会有人好奇直接调用run方法不可以吗?当然是可以的,但那样就不是启动线程了,只是单纯的调用方法而已了,最直观的就是我们用start去启动以后hello thread和hello Man是交替打印可能是打印一堆hello Man再打印hello thread或者相反因为线程的调度是随机的,但如果直接调用run方法的话就只会打印hello thread,因为进入了死循环就出不来了。

第二种方法会涉及到一个名为Runnable的接口,我们定义一个类去实现这个接口然后重新run方法,具体的有点说不清楚来看代码说

java 复制代码
public class Test2 {
    class MyRunnable implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("hello Man");
            }

        }
    }

    public static void main(String[] args) throws InterruptedException {
//实例化MyThread(向上转型)
        Runnable runnable = new MyThread();
//再实例化Thread然后传参数           作为参数
        Thread thread = new Thread(runnable);
        thread.start();
        while (true) {
            Thread.sleep(1000);
            System.out.println("hello test");

        }

    }
}

这里就是使用Runnable对象创建线程对象,但还是要通过实例化Thread来启动线程。这里就不得不提到Thread的另外一种构造方法了,Thread(Runnable t,String name),这个就是使用Runnable对象创建线程对象并命名。

第三种则是用匿名内部类来创建线程,如下

java 复制代码
public class Test3 {
    public static void main(String[] args) {
     Thread thread=new Thread(){
         public void run(){
             while(true){
                 System.out.println("hello test");
                 try {
                     Thread.sleep(1000);
                 } catch (InterruptedException e) {
                     throw new RuntimeException(e);
                 }
             }

         }
     };
     thread.start();
while (true){
    System.out.println("hello test2");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}
    }
}

什么是匿名内部类呢?匿名内部类就是定义在类内部、没有类名、临时快速创建子类实例 的写法。用这种写法就相当于临时创建一个没有名字的 Thread 子类 用完就丢,不用单独写类文件。

第四种也是我最推荐的一种写法就是用lambda来写,代码如下

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

    }
}

为什么推荐用lambad表达式来创建呢?这是因为lambda 更简洁、代码更少、可读性强,专门适配函数式接口,创建线程更优雅。所以平常我们创建线程最好都用lambda表达式来创建线程。

三、中断线程

在多线程编程中,我们常常会遇到这样的场景:启动一个线程后,需要在某个条件满足时让它停止运行------比如用户点击取消按钮、程序执行完毕、出现异常等。但线程的停止绝对不能"暴力",直接强制终止线程可能会导致资源泄漏、数据错乱等问题。今天我们就来聊聊线程中断(Thread Interruption),这是Java中推荐的、优雅且安全的线程停止方式。

很多初学者会误以为"中断线程"就是直接终止线程的运行,其实这是一个常见的误区。Java中的线程中断,本质上是给线程发送一个"中断信号",告诉线程:"你该停止运行了(如果方便的话)"。线程本身拥有自主决定权:它可以选择立即响应中断,停止运行;也可以选择忽略中断,继续执行;还可以选择先完成当前的任务,再停止。这种设计既保证了线程停止的灵活性,也避免了强制终止线程带来的风险。举个简单的例子:就像老师喊学生"下课了,停止学习",学生可以立即放下书本(响应中断),也可以先写完这道题再停(延迟响应),甚至假装没听见(忽略中断)------但老师只是传递了"停止"的信号,没有直接抢走学生的书本(强制终止)。

Java提供了三个核心方法来操作线程中断,都定义在Thread类中,我们逐个拆解,结合实例理解

3.1 interrupt():发送中断信号

作用:给目标线程发送一个中断信号,将线程的"中断状态"设为true。注意:这个方法不会直接终止线程,只是告诉线程"该中断了"。

java 复制代码
public class ThreadInterruptionDemo {
    public static void main(String[] args) {
        // 用Lambda创建线程(推荐方式)
        Thread thread = new Thread(() -> {
            while (true) {
                System.out.println("线程正在运行...");
                try {
                    // 线程休眠1秒,模拟业务逻辑
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // 捕获中断异常,后续处理中断逻辑
                    System.out.println("线程收到中断信号!");
                    break; // 响应中断,退出循环,停止线程
                }
            }
        });
        
        thread.start(); // 启动线程
        
        // 主线程休眠3秒后,给子线程发送中断信号
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt(); // 发送中断信号
        System.out.println("主线程已发送中断信号");
    }
}

3.2 isInterrupted():判断线程是否被中断

作用:判断当前线程的中断状态(是否收到了中断信号),返回boolean值。

特点:不会清除中断状态------如果线程被中断,调用该方法会返回true,再次调用依然返回true(除非手动清除中断状态)。

java 复制代码
Thread thread = new Thread(() -> {
    // 用isInterrupted()判断中断状态,决定是否继续运行
    while (!Thread.currentThread().isInterrupted()) {
        System.out.println("线程正在运行...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("线程收到中断信号!");
            // 手动清除中断状态(可选,根据需求决定)
            Thread.currentThread().interrupt(); // 重新设置中断状态为true
        }
    }
    System.out.println("线程已停止运行");
});

这里有个细节:当线程在sleep()、wait()等方法中被中断时,会抛出InterruptedException,并且自动清除中断状态(此时isInterrupted()会返回false)。如果我们希望线程继续响应中断,需要在catch块中手动调用interrupt(),重新设置中断状态为true。

3.3 interrupted():判断并清除中断状态

作用:判断当前线程是否被中断,返回boolean值,并且会清除中断状态------如果线程被中断,调用一次返回true,再次调用就会返回false(因为中断状态被清除了)。

注意:该方法是静态方法,作用于"当前线程",而不是调用该方法的线程对象。

java 复制代码
Thread thread = new Thread(() -> {
    while (true) {
        // 判断当前线程是否被中断,同时清除中断状态
        if (Thread.interrupted()) {
            System.out.println("线程收到中断信号,中断状态已清除");
            break;
        }
        System.out.println("线程正在运行...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            System.out.println("线程在休眠时被中断");
            // 抛出异常后,中断状态已被清除,需要重新中断
            Thread.currentThread().interrupt();
        }
    }
});

这便是中断线程的三个方法,什么时候去中断线程?举一个例子线程在循环当中执行业务逻辑(比如定时任务、数据处理),可以用isInterrupted()判断中断状态,一旦收到中断信号,就退出循环,释放资源,停止线程。

关键:在循环条件中加入中断判断,确保线程能及时响应中断。

线程中断是多线程编程中必须掌握的知识点,掌握它,才能写出安全、优雅、可维护的多线程代码。

四、等待线程

这里将的是最基础的join方法也是在Thread类里面的方法,它的作用是在一个线程启动以后为了保证线程安全问题,要让几个线程按顺序来执行,这个时候就需要join方法了它就是让别的线程等待自己执行完了他再来执行

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

        t.join();
        System.out.println("main 线程结束");
    }
}

这里的main线程就是要等t线程执行完成后才会执行,关于join方法也是可以传参数的,传的就是一个以毫秒为单位的时间,意思是我就等你这么久如果这么久以后你还没有执行完我就继续执行下去不等你了。

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

        t.join(3000);
        System.out.println("main 线程结束");
    }
}

用上面的代码做一点点修改,就是main线程只等t线程3s,3s后你还没有执行完我也不管了。我就是要执行。

五、线程的休眠

线程的休眠是多线程编程中最常用的操作之一,核心作用是让当前线程暂停执行一段时间,释放CPU资源,给其他线程执行机会。在Java中,通过Thread类的sleep()方法实现线程休眠,用法简单但有几个关键细节需要注意。

sleep()是Thread类的静态方法,有两个重载形式:一是Thread.sleep(long millis),单位为毫秒;二是Thread.sleep(long millis, int nanos),可精确到纳秒(实际精度受系统影响)。调用后,当前线程会进入阻塞状态,休眠指定时间后自动唤醒,恢复到就绪状态等待CPU调度。

核心注意事项:1. 休眠时间是"近似值",受系统调度影响,实际休眠时间可能略长;2. 休眠时不会释放对象锁;3. 休眠过程中线程可被中断,会抛出InterruptedException,需捕获处理。

相信大家对于这个并不陌生,在上面的示例代码当中基本都有这个并且也都捕获了,如果没有捕获的话是不能通过编译的,wait方法也是同样要捕获的,这是因为这两个方法都会造成线程的阻塞都会抛出InterruptedException异常。关于wait方法会在后面的文章和大家见面的,这里也是老规矩放一段示例代码。

java 复制代码
public class ThreadSleepDemo {
    public static void main(String[] args) {
        // 线程1:每隔1秒打印一次
        Thread thread1 = new Thread(() -> {
            for (int i = 1; i <= 3; i++) {
                System.out.println("线程1执行:" + i);
                try {
                    Thread.sleep(1000); // 休眠1秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    return; // 中断后停止线程
                }
            }
        });

        // 线程2:休眠2秒后执行
        Thread thread2 = new Thread(() -> {
            try {
                Thread.sleep(2000); // 先休眠2秒
                System.out.println("线程2延迟2秒执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        thread1.start();
        thread2.start();
    }
}

六、总结

到这里,Java 线程基础的核心知识点就全部给大家讲完了,咱们简单捋一遍。

创建线程四种方式:继承 Thread 类、实现 Runnable 接口、匿名内部类、Lambda 表达式。日常开发最推荐用 Lambda,代码简洁优雅,可读性也最强。一定要记住,启动线程必须用 start (),直接调用 run () 只是普通方法调用,不是真正启动线程。

中断线程不是暴力杀死线程,只是给线程发一个停止信号,用好 interrupt ()、isInterrupted ()、interrupted () 这三个方法,就能优雅安全地停止线程,千万别用废弃的 stop () 方法。

等待线程靠 join (),可以让一个线程等待另一个执行完再运行,带参数的 join 还能控制等待时长,灵活控制线程执行顺序。

线程休眠用 sleep (),能让线程暂停一段时间、释放 CPU 资源,使用时必须捕获中断异常,它和等待、中断的配合也是多线程基础里的重点。

这些都是 Java 多线程最入门、最核心的内容,把创建、启动、中断、等待、休眠这几块吃透,就算真正踏入多线程的大门了,后面学习更复杂的线程知识也会轻松很多。

相关推荐
清水白石00812 小时前
从“点一下导出”到生产级任务队列:Python 异步导出系统设计全景解析
java·数据库·python
Mahir0812 小时前
Spring 核心原理:IoC/DI 与 Bean 生命周期全景解析
java·后端·spring·面试·bean生命周期·控制反转ioc·依赖注入di
weixin_4896900212 小时前
NAS部署实测:Solon vs Spring Boot,从内存到包体积的“降维打击”
java·spring boot·后端
我命由我1234512 小时前
Android 开发问题:TextView 内容超过宽度时,默认不会换行
android·开发语言·java-ee·android studio·android jetpack·android-studio·android runtime
计算机安禾12 小时前
【c++面向对象编程】第36篇:析构函数应永远不抛出异常——原因与最佳实践
开发语言·c++
tongluowan00712 小时前
怎么保证缓存和数据库的一致性
java·数据库·缓存·一致性
一条泥憨鱼12 小时前
【Java 进阶】LinkedHashMap 与 TreeMap
java·开发语言·数据结构·笔记·后端·学习
ゆづき12 小时前
假如编程语言们有外号
java·c语言·c++·python·学习·c#·生活
凤山老林12 小时前
63-Java LinkedList(链表)
java·开发语言·链表