Java多线程实现方式详解


Java提供了多种实现多线程的方式,每种方式各有特点,适用于不同的场景。下面将全面介绍Java中实现多线程的几种主要方法。

1. 继承Thread类

特点

  • 最简单直接的方式
  • 通过继承Thread类并重写run()方法
  • 单继承限制(Java不允许多继承)

实现步骤

  1. 创建Thread的子类
  2. 重写run()方法
  3. 创建子类实例
  4. 调用start()方法启动线程

示例代码

class MyThread extends Thread {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "运行: " + i);
            try {
                sleep((int)(Math.random() * 1000)); // 随机休眠
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread t1 = new MyThread("线程A");
        MyThread t2 = new MyThread("线程B");
        t1.start(); // 启动线程
        t2.start();
    }
}

注意事项

  • 直接调用run()方法不会启动新线程,只是在当前线程中执行
  • 必须调用start()方法才能启动新线程
  • 每个线程只能start()一次

2. 实现Runnable接口

特点

  • 更灵活,避免了单继承的限制
  • 适合多个线程处理同一资源的情况
  • 可以方便地使用线程池

实现步骤

  1. 创建实现Runnable接口的类
  2. 实现run()方法
  3. 创建Thread对象并传入Runnable实例
  4. 调用Thread对象的start()方法

示例代码

class MyRunnable implements Runnable {
    private String name;

    public MyRunnable(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "运行: " + i);
            try {
                Thread.sleep((int)(Math.random() * 1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class RunnableDemo {
    public static void main(String[] args) {
        MyRunnable r1 = new MyRunnable("线程A");
        MyRunnable r2 = new MyRunnable("线程B");

        new Thread(r1).start();
        new Thread(r2).start();

        // Java 8 Lambda表达式简化写法
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Lambda线程运行: " + i);
            }
        }).start();
    }
}

3. 实现Callable接口

特点

  • 可以返回执行结果
  • 可以抛出异常
  • 通常与FutureTask或ExecutorService配合使用

实现步骤

  1. 创建实现Callable接口的类
  2. 实现call()方法
  3. 创建FutureTask实例包装Callable对象
  4. 创建Thread对象并传入FutureTask实例
  5. 调用start()方法启动线程
  6. 通过FutureTask获取结果

示例代码

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<Integer> {
    private String name;

    public MyCallable(String name) {
        this.name = name;
    }

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "运行: " + i);
            sum += i;
            Thread.sleep((int)(Math.random() * 1000));
        }
        return sum;
    }
}

public class CallableDemo {
    public static void main(String[] args) throws Exception {
        MyCallable c1 = new MyCallable("线程A");
        MyCallable c2 = new MyCallable("线程B");

        FutureTask<Integer> ft1 = new FutureTask<>(c1);
        FutureTask<Integer> ft2 = new FutureTask<>(c2);

        new Thread(ft1).start();
        new Thread(ft2).start();

        System.out.println("线程A结果: " + ft1.get()); // 阻塞直到获取结果
        System.out.println("线程B结果: " + ft2.get());
    }
}

4. 使用线程池

特点

  • 减少线程创建和销毁的开销
  • 提高系统资源利用率
  • 提供更强大的线程管理功能

常用线程池

  1. FixedThreadPool:固定大小线程池
  2. CachedThreadPool:可缓存线程池
  3. ScheduledThreadPool:定时任务线程池
  4. SingleThreadExecutor:单线程线程池

实现步骤

  1. 创建线程池
  2. 提交任务(Runnable或Callable)
  3. 关闭线程池

示例代码

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

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

        // 提交任务
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            pool.execute(() -> {
                System.out.println("任务" + taskId + "由" + Thread.currentThread().getName() + "执行");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        // 关闭线程池
        pool.shutdown();
        try {
            // 等待所有任务完成
            if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
                pool.shutdownNow();
            }
        } catch (InterruptedException e) {
            pool.shutdownNow();
        }
    }
}

5. Fork/Join框架(Java 7+)

特点

  • 适合分而治之的任务
  • 工作窃取算法提高效率
  • 自动利用多核处理器

核心类

  • ForkJoinPool:特殊线程池
  • RecursiveTask:有返回值的任务
  • RecursiveAction:无返回值的任务

示例代码

import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;

class SumTask extends RecursiveTask<Long> {
    private static final int THRESHOLD = 1000; // 阈值
    private long[] array;
    private int start;
    private int end;

    public SumTask(long[] array, int start, int end) {
        this.array = array;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        // 如果任务足够小,直接计算
        if (end - start <= THRESHOLD) {
            long sum = 0;
            for (int i = start; i < end; i++) {
                sum += array[i];
            }
            return sum;
        }

        // 任务太大,拆分为两个子任务
        int middle = (start + end) / 2;
        SumTask leftTask = new SumTask(array, start, middle);
        SumTask rightTask = new SumTask(array, middle, end);

        // 并行执行子任务
        leftTask.fork();
        rightTask.fork();

        // 合并结果
        return leftTask.join() + rightTask.join();
    }
}

public class ForkJoinDemo {
    public static void main(String[] args) {
        // 创建测试数组
        long[] array = new long[10000];
        for (int i = 0; i < array.length; i++) {
            array[i] = i + 1;
        }

        // 创建ForkJoinPool
        ForkJoinPool pool = new ForkJoinPool();
        SumTask task = new SumTask(array, 0, array.length);

        // 提交任务并获取结果
        long result = pool.invoke(task);
        System.out.println("计算结果: " + result);
    }
}

6. 线程同步与通信

6.1 同步方法

public synchronized void add(int value) {
    this.count += value;
}

6.2 同步代码块

public void add(int value) {
    synchronized(this) {
        this.count += value;
    }
}

6.3 使用Lock对象

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private int count = 0;
    private Lock lock = new ReentrantLock();

    public void add(int value) {
        lock.lock();
        try {
            this.count += value;
        } finally {
            lock.unlock();
        }
    }
}

6.4 线程通信(wait/notify)

class Message {
    private String message;
    private boolean empty = true;

    public synchronized String read() {
        while (empty) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        empty = true;
        notifyAll();
        return message;
    }

    public synchronized void write(String message) {
        while (!empty) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        empty = false;
        this.message = message;
        notifyAll();
    }
}

7. 选择多线程实现方式的建议

  1. 简单任务:实现Runnable接口或使用Lambda表达式
  2. 需要返回值:使用Callable和Future
  3. 大量短期任务:使用线程池(ExecutorService)
  4. 分治任务:使用Fork/Join框架
  5. 定时任务:使用ScheduledExecutorService
  6. 避免直接继承Thread:除非需要重写Thread的方法

8. 多线程编程注意事项

  1. 线程安全:注意共享资源的同步访问
  2. 死锁避免:避免多个线程互相等待对方释放锁
  3. 性能考量:线程创建和上下文切换有开销
  4. 资源释放:确保线程结束时释放所有资源
  5. 异常处理:线程中的异常通常不会传播到主线程

掌握这些多线程实现方式后,你可以根据具体需求选择最适合的方法来构建高效、可靠的并发Java应用程序。

,

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注