当前位置: 首页 > news >正文

线程的多种创建方式和使用

一、线程的多种创建方式

在 Java 中,创建线程有多种方式,每种方式都有其特定的应用场景。以下是 Java 中常用的几种创建线程的方式,以及它们的具体实现:

1、通过继承 Thread 类创建线程

Java 中的 Thread 类提供了一个可执行的线程对象。通过继承 Thread 类并重写其 run() 方法来定义线程的执行体。

示例代码1:
class MyThread extends Thread {
    @Override
    public void run() {
        // 线程要执行的代码
        System.out.println("Thread is running");
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyThread thread = new MyThread(); // 创建线程对象
        thread.start(); // 启动线程
    }
}
示例代码2:使用匿名内部类
public class AnonymousThreadExample {
    public static void main(String[] args) {
        // 创建线程并通过匿名内部类继承 Thread 类
        Thread thread = new Thread() {
            @Override
            public void run() {
                // 线程要执行的代码
                System.out.println("Thread is running using Anonymous Thread");
            }
        };
        thread.start();  // 启动线程
    }
}
说明:
  • 优点:简单直接,适用于简单的线程创建。
  • 缺点:Java 中类只能继承一个类,因此如果已经继承了其他类,不能再继承 Thread 类。

2、通过实现 Runnable 接口创建线程

实现 Runnable 接口是一种更加灵活的方式,它可以避免 Java 单继承的限制。通过实现 Runnable 接口的 run() 方法来定义线程的执行体,再通过 Thread 类来启动该线程。

示例代码:
class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 线程要执行的代码
        System.out.println("Thread is running");
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable(); // 创建 Runnable 实现类对象
        Thread thread = new Thread(myRunnable); // 将 Runnable 实现类传递给 Thread
        thread.start(); // 启动线程
    }
}
说明:
  • 优点:比继承 Thread 更灵活,可以同时继承其他类。适用于多线程共享同一资源的情况。
  • 缺点:需要通过 Thread 对象来启动线程,不能直接调用 start() 方法。

3、通过实现 Callable 接口创建线程(可返回值)

Callable 接口与 Runnable 类似,但它允许任务执行时返回结果,可以处理异常。通常与 ExecutorService 配合使用,能够提交任务并获得执行结果。

示例代码:
import java.util.concurrent.*;

class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        // 线程要执行的代码
        return "Thread is running and returning result";
    }
}

public class CallableExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable();
        ExecutorService executorService = Executors.newCachedThreadPool(); // 创建线程池
        Future<String> future = executorService.submit(myCallable); // 提交任务

        String result = future.get(); // 获取任务的执行结果
        System.out.println(result); // 输出结果

        executorService.shutdown(); // 关闭线程池
    }
}
说明:
  • 优点:能够返回任务执行结果,适用于需要计算并返回结果的场景。
  • 缺点:需要使用 ExecutorService 来执行任务,相比 Runnable 略显复杂。

4、通过 ExecutorService 创建线程池

线程池是一种管理线程的方式,通过 ExecutorService 可以创建线程池并提交任务,线程池会自动管理线程的创建、销毁和复用。通过线程池可以有效地管理多线程任务,避免频繁的线程创建销毁操作。

示例代码:
import java.util.concurrent.*;

class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println("Thread is running from ExecutorService");
    }
}

public class ExecutorServiceExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3); // 创建固定大小的线程池

        for (int i = 0; i < 5; i++) {
            executorService.submit(new MyTask()); // 提交任务给线程池
        }

        executorService.shutdown(); // 关闭线程池
    }
}
说明:
  • 优点:可以管理大量线程,自动复用线程,提高性能。适用于高并发的场景。
  • 缺点:需要额外的线程池管理,使用稍微复杂。

5、通过 ForkJoinPool 创建线程(适用于分治任务)

ForkJoinPool 是一种专门用于处理大规模并行任务的线程池,特别适合分治型任务的执行。它通过将任务分割为多个子任务并行执行来提高性能。

示例代码:
import java.util.concurrent.*;

class MyForkJoinTask extends RecursiveTask<Integer> {
    @Override
    protected Integer compute() {
        // 线程要执行的代码
        return 1; // 示例返回值
    }
}

public class ForkJoinPoolExample {
    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool(); // 创建 ForkJoinPool

        MyForkJoinTask task = new MyForkJoinTask();
        ForkJoinTask<Integer> result = forkJoinPool.submit(task); // 提交任务
        System.out.println("Result: " + result.join()); // 获取结果

        forkJoinPool.shutdown(); // 关闭线程池
    }
}
说明:
  • 优点:适合计算密集型任务和分治型任务,能够有效处理大量子任务。
  • 缺点:相对于其他线程池,使用场景更为特殊。

6、通过 Lambda 表达式创建线程(简化代码)

Java 8 引入了 Lambda 表达式,可以简化线程的创建过程。通常与 Runnable 接口配合使用,能够在一行代码中定义线程的执行内容。

示例代码:
public class LambdaThreadExample {
    public static void main(String[] args) {
        // 使用 Lambda 表达式创建线程
        Thread thread = new Thread(() -> {
            System.out.println("Thread is running using Lambda");
        });
        thread.start(); // 启动线程
    }
}
说明:
  • 优点:代码简洁,减少了冗长的类定义,适用于快速创建简单线程。
  • 缺点:仅适用于 Runnable 接口场景,对于其他接口无法使用。

7、总结

Java 提供了多种方式来创建线程,每种方式都有其特定的优势和适用场景:

  1. 继承 Thread 类:适合简单的线程创建,但不能继承其他类。
  2. 实现 Runnable 接口:适合多线程共享资源的情况,比继承 Thread 更灵活。
  3. 实现 Callable 接口:适合需要返回结果的线程任务,通常与 ExecutorService 配合使用。
  4. 使用 ExecutorService:适合管理大量线程,自动复用线程,提高系统性能。
  5. 使用 ForkJoinPool:适合大规模并行计算和分治型任务。
  6. 使用 Lambda 表达式:适合快速创建简单线程,代码简洁。

选择哪种方式,取决于具体应用场景和任务需求。在实际开发中,通常推荐使用 ExecutorServiceForkJoinPool 来管理线程,以提高代码的可维护性和性能。

二、Thread类最常用个方法

1. start()

功能:启动一个新线程,使其进入就绪状态,等待操作系统调度执行。

  • 作用:启动线程后,JVM 会自动调用 run() 方法,并在新的线程中执行。
  • 注意:线程一旦启动后,不能再次启动。如果调用 start() 方法多次,会抛出 IllegalThreadStateException 异常。
Thread thread = new Thread(() -> System.out.println("Thread is running"));
thread.start();  // 启动线程

2. run()

功能:线程的执行体方法,定义线程启动后要执行的代码。

  • 作用:当调用 start() 启动线程时,JVM 会调用 run() 方法。通常情况下,run() 方法会被覆盖,定义线程的具体执行逻辑。
  • 注意:直接调用 run() 方法只是将 run() 方法作为普通方法调用,并不会启动新的线程,仍然是在主线程中执行。
@Override
public void run() {
    System.out.println("Thread is running");
}

3. sleep(long millis)

功能:让当前线程暂停指定时间(以毫秒为单位),进入 休眠状态,不会占用 CPU 资源。

  • 作用:使当前线程暂时进入休眠状态,休眠结束后,线程会回到就绪状态,等待操作系统调度。
  • 注意sleep() 方法是静态的,作用于调用它的当前线程,且可以抛出 InterruptedException 异常。
try {
    Thread.sleep(1000);  // 当前线程暂停 1000 毫秒(1秒)
} catch (InterruptedException e) {
    e.printStackTrace();
}

4. join()

功能:使当前线程等待目标线程完成后再继续执行,常用于 线程同步

  • 作用join() 方法会使当前线程等待调用它的线程(即目标线程)执行完毕后再继续执行。
  • 注意:可以指定等待时间,等待超时后 join() 方法会返回。
// 创建子线程对象
Thread thread = new Thread(() -> System.out.println("Thread is running"));
// 执行子线程
thread.start();
try {
    thread.join();  // 当前线程(即:主线程)等待子线程执行完
} catch (InterruptedException e) {
    e.printStackTrace();
}

5. interrupt()

功能:中断一个线程,设置线程的中断标志为 true,通知线程终止。

  • 作用:如果线程正在执行 sleep()wait()join() 等阻塞操作,interrupt() 会使这些操作抛出 InterruptedException,从而让线程终止或者退出阻塞。
  • 注意interrupt() 只是设置线程的中断标志,不会立即停止线程的执行。线程需要在合适的地方自行判断 Thread.interrupted() 或捕获 InterruptedException 来响应中断。
Thread thread = new Thread(() -> {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        System.out.println("Thread was interrupted");
    }
});
thread.start();
thread.interrupt();  // 中断线程

6. isAlive()

功能:判断线程是否已经启动并正在执行。

  • 作用:返回线程是否处于活动状态。如果线程处于 新建状态已死亡状态,返回 false;否则返回 true
Thread thread = new Thread(() -> System.out.println("Thread is running"));
thread.start();
System.out.println(thread.isAlive());  // true

7. getName()

功能:获取线程的名称。

  • 作用:返回当前线程的名字,通常是由 JVM 自动分配的,如 Thread-0Thread-1 等,也可以手动通过 setName() 方法来设置。
Thread thread = new Thread(() -> System.out.println(Thread.currentThread().getName()));
thread.start();  // 输出:Thread-0

8. setName(String name)

功能:设置线程的名称。

  • 作用:给当前线程指定一个名称。为线程设置名称有助于在调试和日志中跟踪线程。
Thread thread = new Thread(() -> System.out.println(Thread.currentThread().getName()));
thread.setName("CustomThread");
thread.start();  // 输出:CustomThread

9. getId()

功能:获取线程的唯一标识符。

  • 作用:返回当前线程的唯一 ID,通常用于调试和标识线程。
Thread thread = new Thread(() -> System.out.println(Thread.currentThread().getId()));
thread.start();  // 输出线程的 ID,例如:1

10. setPriority(int priority)

功能:设置线程的优先级,影响线程获取 CPU 时间片的频率。

  • 作用setPriority() 方法允许你设置线程的优先级(从 1 到 10,默认值为 5)。高优先级线程通常会更早地获得 CPU 时间片,但这也依赖于操作系统的线程调度策略。
Thread thread = new Thread(() -> System.out.println("Thread is running"));
thread.setPriority(Thread.MAX_PRIORITY);  // 设置为最高优先级
thread.start();

11. yield()

功能:使当前线程放弃 CPU 时间片,暂时让出 CPU 资源,允许其他线程执行。

  • 作用yield() 是一个静态方法,当前线程会让出 CPU 使用权,但是不会完全让出控制权,仍然可以继续执行。具体行为依赖于操作系统的线程调度策略。
Thread.yield();  // 当前线程暂停,让出 CPU 资源

12. currentThread()

功能:获取当前正在执行的线程对象。

  • 作用:返回代表当前正在执行线程的 Thread 对象。常用于获取当前线程的状态、名称等信息。
Thread current = Thread.currentThread();
System.out.println(current.getName());  // 输出当前线程的名称

三. 线程的生命周期

相关文章:

  • QML 自定义矩形框Rectangle,实现四个边框自定义大小
  • 反射机制的简单示例
  • pytorch预训练模型下载保存路径更改
  • 【干货教程】DeepSeek R1+Open WebUI构建RAG检索增强知识库的实战教程
  • 《DeepSeek Janus Pro 7B:多模态人工智能大模型部署全攻略》
  • 用 UniApp 打造新颖美观的弹出框
  • C#多线程
  • UEFI Spec 学习笔记---11 - Protocols — UEFI Driver Model(1)
  • 嵌入式音视频开发(二)ffmpeg音视频同步
  • 大一计算机的自学总结:前缀树(字典树、Trie树)
  • 【Ai】辅助编程利器:Cline、Continue
  • 大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(1)
  • #渗透测试#批量漏洞挖掘#Apache Log4j反序列化命令执行漏洞
  • 基于AdaIN的实时图像风格迁移-照片艺术化
  • 初识LLMs
  • 第四十三篇——零和博弈(鞍点理论):如何找到双方的平衡点?
  • 【Linux系统】生产者消费者模型:基于阻塞队列 BlockingQueue
  • 【笔记】LLM|Ubuntu22服务器极简本地部署DeepSeek+API使用方式
  • 使用apt-rdepends制作软件离线deb安装包
  • 网站搭建基本流程
  • 日本儿童人数已连续44年减少,少子化问题越发严重
  • 这样喝酸奶相当于在喝糖水,会导致娃龋齿、肥胖
  • 当Z世代与传统戏曲在春日校园相遇
  • 局势紧张之际,伊朗外长下周访问巴基斯坦和印度
  • 全国共有共青团员7531.8万名,共青团组织439.7万个
  • 五一去哪玩?“时代交响”音乐会解锁艺术假期