Java中的线程

什么是线程?

  • 线程(Thread)是程序执行流的最小单位。
  • 在 Java 中,一个线程代表一个独立的执行路径,它运行在进程(Process)内部。
  • 进程 vs 线程:
    • 进程是操作系统分配资源的基本单位(如一个 Java 应用就是一个进程)。
    • 线程是 CPU 调度的基本单位,多个线程共享同一个进程的内存空间(堆、方法区),但各自拥有独立的栈和程序计数器。

多线程程序在更低一层扩展了多任务的概念:单个程序看起来在同时完成多个任务。每个任务在一个线程(thread)中执行,线程是控制线程的简称。如果一个程序可以同时运行多个线程,则称这个程序是多线程的(multithreaded)。

在Java中创建线程

线程的创建有三种方式:

2.1 继承Thread类,重写run方法

  1. 创建一个继承Thread的类,重写run方法
  2. 在main方法中创建该类的对象,对象使用start方法启动线程,并开始执行该线程的任务
  3. 如果直接调用run方法,只会让当前线程执行任务
public class ThreadTest {
    private static final int STEP = 100;

    public static void main(String[] args) {
        MyJob job = new MyJob();
        job.start();
        for (int i = 0; i < STEP; i++) {
            System.out.println("main run: " + i);
        }
    }
}

class MyJob extends Thread {
    private static final int STEP = 100;

    @Override
    public void run() {
        for (int i = 0; i < STEP; i++) {
            System.out.println("job run: " + i);
        }
    }
}

2.2 实现Runnable接口,重写run方法

  1. 创建一个类实现Runnable接口,重写run方法
  2. 在main方法中创建该类的对象
  3. 创建Thread类的对象,构造方法中传入Runnable接口的实现类对象
  4. Thread类的对象使用start方法启动线程,并开始执行该线程的任务
  5. 同样,如果直接调用run方法,只会让当前线程执行任务
public class RunnableTest {
    private static final int STEP = 100;

    public static void main(String[] args) {
        MyAnotherJob job = new MyAnotherJob();
        Thread t = new Thread(job);
        t.start();
        for (int i = 0; i < STEP; i++) {
            System.out.println("main run: " + i);
        }
    }
}

class MyAnotherJob implements Runnable {
    private static final int STEP = 100;

    @Override
    public void run() {
        for (int i = 0; i < STEP; i++) {
            System.out.println("job run: " + i);
        }
    }
}

2.3 实现Callable接口,重写call方法,配合FutureTask使用

Callable一般用于有返回结果的非阻塞的执行方法
同步非阻塞

  1. 创建一个类实现 Callable<V> 接口
  • 泛型 V 表示 call() 方法的返回类型(如 String、Integer 等)。
  • 重写 call() 方法,在其中编写需要在线程中执行的逻辑,并返回结果。
  1. 创建 Callable 实例
  • 即你自定义的实现了 Callable 的类的对象。
  1. 将 Callable 实例包装进 FutureTask
  • FutureTask 是一个包装器,它实现了 Runnable 和 Future 接口,既能被线程执行,又能获取结果。
  1. 创建 Thread 对象,传入 FutureTask
  • 因为 FutureTask 实现了 Runnable,所以可以作为 Thread 的构造参数。
  1. 启动线程(调用 start())
  2. 调用 FutureTask.get() 获取线程执行结果
  • get() 方法会阻塞当前线程,直到任务完成并返回结果。
  • 如果任务抛出异常,get() 会抛出 ExecutionException。
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

class MyTask implements Callable<String> {
    private final long duration; // 模拟耗时操作的时间(毫秒)

    public MyTask(long duration) {
        this.duration = duration;
    }

    @Override
    public String call() throws Exception {
        System.out.println("任务开始执行...");
        Thread.sleep(duration); // 模拟耗时操作
        return "任务完成,耗时 " + duration + " 毫秒";
    }
}

public class CallableExample {
    public static void main(String[] args) {
        Callable<String> callable = new MyTask(2000);

        FutureTask<String> futureTask = new FutureTask<>(callable);

        Thread thread = new Thread(futureTask, "MyThread");

        thread.start();

        System.out.println("主线程继续执行其他任务...");

        try {
            String result = futureTask.get();
            System.out.println("线程返回结果: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

多线程三种实现方式的对比

优点 缺点
继承Thread类 实现简单,可以直接使用Thread类中的方法 不能再继承其他类,扩展性较差
实现Runnable接口 扩展性强,实现接口的同时可以继承其他类 编程比较复杂,不能使用Thread类的其他方法
实现Callable接口 扩展性强,实现接口的同时可以继承其他类 编程比较复杂,不能使用Thread类的其他方法

文章摘自:https://www.cnblogs.com/vitastic/p/19820800