Java 中创建线程几种方式

目录

概述

[一. 继承Thread类](#一. 继承Thread类)

[1. 特点](#1. 特点)

[2. 注意事项](#2. 注意事项)

[3. 代码示例](#3. 代码示例)

[二. 实现Runnable接口](#二. 实现Runnable接口)

[1. 特点](#1. 特点)

[2. 注意事项](#2. 注意事项)

[3. 代码示例](#3. 代码示例)

[三. 实现Callable接口](#三. 实现Callable接口)

[1. 特点](#1. 特点)

[2. 注意事项](#2. 注意事项)

[3. 代码示例](#3. 代码示例)


概述

在Java中,线程(Thread)是程序执行的最小单元,它允许程序在同一时间执行多个任务。Java中的线程可以由Thread类创建,也可以通过实现Runnable接口或Callable接口创建。

java中线程几种状态:

  1. 新建(New) : 线程对象已经被创建,但还没有调用start()方法。

  2. 可运行(Runnable) : 线程已经调用了start()方法,但还没有获得CPU时间片执行。可运行状态包括了操作系统线程的就绪(Ready)和运行(Running)状态。

  3. 阻塞(Blocked): 线程因为等待监视器锁而被阻塞,无法继续执行。

  4. 等待(Waiting) : 线程通过调用wait()join()LockSupport.park()等方法进入等待状态,需要其他线程通知或中断后才可能继续执行。

  5. 超时等待(Timed Waiting) : 线程通过调用带有超时参数的sleep()wait()join()LockSupport.parkNanos()LockSupport.parkUntil()等方法进入超时等待状态,超时后会自动唤醒。

  6. 终止(Terminated) : 线程的run()方法执行结束,或者因异常退出了run()方法。

线程的状态枚举类 State

一. 继承Thread类

通过继承Thread类并重写run方法来创建线程,本质上继承Thread类实现线程的方式也是通过实现 Runnable 接口,

1. 特点

  • 简单性:实现线程的代码简单直观。
  • 局限性 :由于Java的单继承特性,如果一个类已经继承了其他类,就不能再继承Thread类。

2. 注意事项

  • 不要调用Thread类的run方法 :应该重写run方法,而不是直接调用它。
  • 使用start方法启动线程start方法会创建一个新的线程,并调用run方法。
  • 线程安全:如果线程之间需要共享数据,需要考虑线程安全问题。

3. 代码示例

java 复制代码
package com.demo.thread;

/**
 * 文件名:ThreadDemo
 * 创建者:
 * 创建时间:2024-09-22
 * 描述: 通过继承 Thread 类并重写run方法来创建线程
 */
public class ThreadDemo extends Thread{
    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName()+"-执行run方法");
    }
}

/**
 * 测试类
 */
class MainThread{
    public static void main(String[] args) {
        //1.第一种方式创建线程
        Thread thread = new Thread(new ThreadDemo(),"线程1");
        thread.start(); //启动线程

        //2.第二种方式创建线程
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"-执行run方法");
            }
        },"线程2");
        thread2.start(); //启动线程
    }
}

二. 实现Runnable接口

实现 Runnable 接口是Java中创建线程的另一种常见方式,这种方式更加灵活,因为它允许类继承其他类的同时实现线程功能。Runnable 接口定义了一个 run 方法,这是线程执行的入口点。

1. 特点

  • 灵活性 :实现 Runnable 接口允许类继承其他类,同时实现多线程功能。
  • 线程安全 :如果多个线程共享同一个 Runnable 实例,需要考虑线程安全问题。

2. 注意事项

  • 不要在 run 方法中调用 Thread.currentThread().stop():这是不推荐的做法,因为它可能会导致程序处于不确定状态。
  • 异常处理 :在 run 方法中妥善处理异常,避免线程意外终止。
  • 资源清理:如果线程使用了一些资源(如文件句柄、数据库连接等),确保在线程结束时释放这些资源。

3. 代码示例

java 复制代码
package com.demo.thread;

/**
 * 文件名:RunnableDemo
 * 创建者:
 * 创建时间:2024-09-22
 * 描述:实现 Runnable接口创建线程
 */
public class RunnableDemo implements Runnable{
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-"+"执行run方法");
    }
}

/**
 * 测试类
 */
class MainDemo{
    public static void main(String[] args) {
        //1.第一种创建线程执行
        Thread thread = new Thread(new RunnableDemo(),"线程1");
        thread.start(); // 启动线程

        //2.第二种创建线程执行
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"-执行run方法");
            }
        },"线程2");
        thread2.start(); // 启动线程

    }
}

三. 实现Callable接口

当我们需要创建一个可以返回结果的线程时,就可以使用实现了Callable接口的方式。Callable接口是在Java 5中引入的,它允许线程执行一个任务并返回一个结果,与Runnable接口相比,Callable接口的call()方法可以返回结果并抛出异常。

Callable 接口与 Runnable 接口类似,但它可以返回一个结果并且能抛出异常。Callable 通常与 FutureTask 一起使用,后者实现了 Runnable 接口,并且包装了 Callable 对象。

1. 特点

  • 返回结果Callable 任务可以返回一个结果,可以通过 FutureTask.get() 方法获取。
  • 抛出异常Callable 任务可以抛出异常,可以通过 FutureTask.get() 方法捕获。

2. 注意事项

  • 异常处理 :在 call 方法中抛出的异常可以通过 FutureTask.get() 方法的调用者捕获。
  • 线程安全 :如果 Callable 任务需要访问共享资源,需要考虑线程安全问题。

3. 代码示例

使用步骤:

  1. 创建一个实现 Callable 接口的类
  2. 实现 Callable 接口的 call 方法,该方法是线程执行的入口点。
  3. 创建 Callable 实例
  4. Callable 实例传递给 FutureTask 的构造器
  5. FutureTask 对象传递给 Thread 类的构造器
  6. 创建 Thread 对象
  7. 调用 Thread 对象的 start 方法来启动线程。
  1. 简单使用

    java 复制代码
    package com.demo.thread;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    /**
     * 文件名:CallableDemo
     * 创建者:
     * 创建时间:2024-09-22
     * 描述:通过 Callable 接口实现线程创建
     */
    public class CallableDemo implements Callable<String> {
        @Override
        public String call() throws Exception {
            return "测试";
        }
    }
    
    /**
     * 测试类
     */
    class MainCallable{
        public static void main(String[] args){
            //1.创建对象
            CallableDemo demo = new CallableDemo();
            FutureTask<String> futureTask = new FutureTask<>(demo);
            //2.创建线程
            Thread thread = new Thread(futureTask);
            thread.start();
            //3.获取返回结果
            String res;
            try {
                res = futureTask.get();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } catch (ExecutionException e) {
                throw new RuntimeException(e);
            }
            System.out.println("获取线程返回结果:"+res);
        }
    }
  2. 通过线程池使用

    java 复制代码
    package com.demo.thread;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    /**
     * 文件名:Main
     * 创建者:
     * 创建时间:2024-09-22
     * 描述:
     */
    public class Main {
        public static void main(String[] args) {
            //1.创建一个线程池
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            //2.创建Callable任务
            Callable<String> callableTask = () -> {
                Thread.sleep(2000); // 模拟长时间运行的任务
                return "测试";
            };
            //3.提交Callable任务并获取Future对象
            Future<String> future = executorService.submit(callableTask);
            try {
                //3.获取异步任务的执行结果
                String result = future.get(); //此方法会阻塞,直到任务执行完成
                System.out.println("任务执行结果: " + result);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //4.关闭线程池
                executorService.shutdown();
            }
        }
    }
相关推荐
YuTaoShao6 分钟前
【LeetCode 热题 100】51. N 皇后——回溯
java·算法·leetcode·职场和发展
null不是我干的25 分钟前
基于黑马教程——微服务架构解析(一)
java·微服务·架构
Bonnie_121529 分钟前
04-netty基础-Reactor三种模型
java·nio·jetty
懂得节能嘛.32 分钟前
【SpringAI实战】ChatPDF实现RAG知识库
java·后端·spring
探索java37 分钟前
Spring 解析 XML 配置文件的过程(从读取 XML 到生成 BeanDefinition)
xml·java·spring·xmlbeanfactory
半旧夜夏1 小时前
Bug猫学习史#1:面向对象
java
wenb1n1 小时前
【安全漏洞】隐藏在HTTP请求中的“隐形杀手”:Host头攻击漏洞深度剖析
java·后端
字节跳跃者1 小时前
SpringBoot AOP + Redis 延时双删功能实战
java·后端
一只叫煤球的猫1 小时前
你真的处理好 null 了吗?——11种常见但容易被忽视的空值处理方式
java·后端·面试
在钱塘江1 小时前
《On Java进阶卷》- 笔记-2-对象传递和返回
java·后端