Java线程的三种创建方式

目录

1.线程的概念

2.进程和线程的对比

3.线程创建的三种方式

1)继承Thread类继承Thread类)

2)实现Runnable接口实现Runnable接口)

3)实现Callable接口实现Callable接口)

三种方法的执行流程


1.线程的概念

线程是程序执行流的最小单位,是进程中的一个独立执行单元。一个进程可以包含多个线程,这些线程共享进程的资源,但各自独立执行。


2.进程和线程的对比


3.线程创建的三种方式

1)继承Thread类

java 复制代码
/**
 * 第一种创建线程的方式,继承Thread类
 * 优点:代码简单
 * 缺点:单继承的局限性,不能再继承其他的类
 */
public class Demo1 {

    public static void main(String[] args) {
        Thread myThread= new MyThread();
        myThread.start();
        for (int i = 0; i < 6; i++) {
            System.out.println("main方法输出:"+ i);
        }
    }
}

class MyThread extends Thread {
    public void run() {
        for (int i = 0; i < 6; i++) {
            System.out.println("子线程输出:" + i);
        }
    }
}

继承关系:MyThread extends Thread

重写方法:必须重写 run() 方法

启动方式:创建对象后调用 start() 方法

执行机制:

start() 会启动新线程,同时调用 run() 方法

直接调用 run() 只会普通方法调用,不会创建新线程

输出结果特点:主线程和子线程的输出会交替出现,顺序不确定

2)实现Runnable接口

java 复制代码
/**
 * 第二种创建线程的方式:实现Runnable接口
 * 优点:只是实现了接口,可以继承其他类,实现其他接口,拓展性强
 * 缺点:需要多一个Runnable对象
 */
public class Demo2 {
    //  目标:掌握多线程的第二种创建方式:实现runnable接口
    public static void main(String[] args) {
        //3.创建线程对象,要把这个线程任务类包装成线程类,把这个对象交给线程去处理
        Thread MyRunnable = new Thread(new MyRunnable());
        MyRunnable.start();
        for (int i = 0; i < 6; i++) {
            System.out.println("Main方法输出:" + i);
        }
    }
}

//1.定义一个线程任务类来实现Runnable接口
class MyRunnable implements Runnable {
    //2.重写run方法,执行线程任务
    @Override
    public void run() {
        for (int i = 0; i < 6; i++) {
            System.out.println("子线程输出:" + i);
        }
    }
}

实现关系:MyRunnable implements Runnable

重写方法:必须实现 run() 方法

线程创建:需要将 Runnable 对象传给 Thread 构造器

优点:

避免单继承限制

适合多个线程执行相同任务

代码结构更清晰

简化写法

1.匿名内部类
java 复制代码
/**
 * 线程创建的第二种方法的匿名内部类写法
 */
public class Demo3 {
    public static void main(String[] args) {
        // 使用匿名内部类创建Runnable对象
        Thread t1 = new Thread(new Runnable(){
            @Override
            public void run() {
                for (int i = 0; i < 6; i++) {
                    System.out.println("子线程输出:" + i);
                }
            }
        });
        t1.start();
        
        for (int i = 0; i < 6; i++) {
            System.out.println("Main方法输出:" + i);
        }
    }
}
2.lambda表达式
java 复制代码
// Java 8+ 可以使用Lambda表达式进一步简化
public class Demo2_Lambda {
    public static void main(String[] args) {
        // 使用Lambda表达式创建Runnable对象
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 6; i++) {
                System.out.println("子线程输出:" + i);
            }
        });
        thread.start();
        
        for (int i = 0; i < 6; i++) {
            System.out.println("Main方法输出:" + i);
        }
    }
}

3)实现Callable接口

java 复制代码
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * 前面两种创建方式重写的run方法都不能直接返回结果
 * JDK5.0提供了Callable接口和FutureTask类来实现
 * 第三种创建方式
 */
public class Demo4 {
    public static void main(String[] args) {
        // 创建Callable接口的实现类对象
        Callable<String> c = new MyCallable(100);
        
        // 4. 把Callable对象封装成真正的线程任务对象FutureTask对象
        FutureTask<String> ft = new FutureTask<>(c);
        
        /**
         * FutureTask对象的作用
         *      a. 本质是一个Runnable线程任务对象,可以交给Thread线程对象处理
         *      b. 可以获取线程执行完毕后的结果
         */
        
        // 5. 把FutureTask对象作为参数传递给Thread对象
        Thread thread = new Thread(ft);
        thread.start();  // 启动线程
        
        // 创建第二个线程
        Callable<String> c1 = new MyCallable(300);
        FutureTask<String> ft1 = new FutureTask<>(c1);
        Thread thread1 = new Thread(ft1);
        thread1.start();
        
        try {
            // 获取第一个线程的执行结果
            // get()方法会阻塞,直到线程执行完成并返回结果
            System.out.println(ft.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        try {
            // 获取第二个线程的执行结果
            System.out.println(ft1.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

// 1. 定义实现类来实现Callable接口
// 泛型参数表示返回值的类型
class MyCallable implements Callable<String> {
    private Integer n;
    
    // 构造方法,传入计算范围
    MyCallable(Integer n) {
        this.n = n;
    }
    
    // 2. 重写call方法,有返回值,可以抛出异常
    @Override
    public String call() throws Exception {
        Integer sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return "1+.....+" + n + "的输出结果为" + sum;
    }
}

实现关系:MyCallable implements Callable<String>

重写方法:必须实现 call() 方法

关键区别:

call() 方法可以有返回值

call() 方法可以抛出异常

run() 方法没有返回值,不能抛出检查异常

执行流程:

Callable实现类 → FutureTask包装 → Thread包装 → start()启动 → get()获取结果

FutureTask 的作用:

适配器模式:将 Callable 转换成 Runnable

结果容器:存储异步计算结果

阻塞获取:get() 方法会等待线程执行完成

三种方法的执行流程

java 复制代码
// 方式1:继承Thread
Thread子类 → start() → JVM调用run()

// 方式2:实现Runnable
Runnable实现类 → 传给Thread → start() → JVM调用run()

// 方式3:实现Callable
Callable实现类 → FutureTask包装 → 传给Thread → start() → get()获取结果

小结

  1. 优先选择实现 Runnable 接口,配合 Lambda 表达式

  2. 需要返回值时使用 Callable + FutureTask

  3. 避免直接继承 Thread 类

  4. 合理使用线程池管理线程资源

相关推荐
脸大是真的好~2 小时前
计算机408基础相关面试题-备用,不推荐
java
云上漫步者2 小时前
深度实战:Rust交叉编译适配OpenHarmony PC——unicode_width完整适配案例
开发语言·后端·rust·harmonyos
小费的部落2 小时前
Excel 在Sheet3中 匹配Sheet1的A列和Sheet2的A列并处理空内容
java·前端·excel
咘噜biu2 小时前
多租户动态数据源插件dynamic-datasource简介
java·mybatisplus·动态数据源·多租户
漫漫求2 小时前
Java内存模型【JMM】、JVM内存模型
java·开发语言·jvm
原来是好奇心2 小时前
深入Spring Boot源码(五):外部化配置与Profile机制深度解析
java·源码·springboot
IT界的奇葩2 小时前
OAuth2 单点登录流程图
java·流程图·oauth2·单点登录·sso
田姐姐tmner2 小时前
Python 全面语法指南
开发语言·python
white-persist2 小时前
【攻防世界】reverse | simple-check-100 详细题解 WP
c语言·开发语言·汇编·数据结构·c++·python·算法