跳转至

多线程

📖 章节简介

本章将介绍Java的多线程编程,包括线程创建、线程同步和线程池的使用。

Java多线程能力图谱

上图梳理了线程创建、同步控制与线程池三类并发能力。

🧵 线程基础

1. 创建线程

Java
// 创建线程的三种方式
public class ThreadCreation {
    // 方式1:继承Thread类
    static class MyThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println("MyThread: " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 方式2:实现Runnable接口
    static class MyRunnable implements Runnable {  // implements 实现接口
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println("MyRunnable: " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        // 使用方式1
        MyThread thread1 = new MyThread();
        thread1.start();

        // 使用方式2
        Thread thread2 = new Thread(new MyRunnable());
        thread2.start();

        // 使用方式3:Lambda表达式(Java 8+)
        Thread thread3 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Lambda Thread: " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread3.start();
    }
}

2. 线程状态

Java
// 线程状态
public class ThreadState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("线程运行中...");
                Thread.sleep(2000);
                System.out.println("线程运行结束");
            } catch (InterruptedException e) {
                System.out.println("线程被中断");
            }
        });

        System.out.println("线程状态: " + thread.getState());  // NEW
        thread.start();
        System.out.println("线程状态: " + thread.getState());  // RUNNABLE

        Thread.sleep(1000);
        System.out.println("线程状态: " + thread.getState());  // TIMED_WAITING

        thread.join();
        System.out.println("线程状态: " + thread.getState());  // TERMINATED
    }
}

🔒 线程同步

1. synchronized关键字

Java
// synchronized使用
public class SynchronizedExample {
    // ⚠️ counter是static变量(类级别共享),所以必须用类锁(static synchronized
    //    或 synchronized(SynchronizedExample.class))来保护。
    //    如果用this锁(实例锁),不同实例之间无法互斥,仍然存在竞态条件。
    private static int counter = 0;

    // 同步方法(类锁 — 保护static变量的正确写法)
    public static synchronized void increment() {  // synchronized 同步方法,保证线程安全
        counter++;
    }

    // 同步代码块(类锁)
    public void decrement() {
        synchronized (SynchronizedExample.class) {
            counter--;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedExample example = new SynchronizedExample();

        // 创建多个线程
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    example.increment();
                }
            });
            threads[i].start();
        }

        // 等待所有线程完成
        for (Thread thread : threads) {
            thread.join();
        }

        System.out.println("最终计数: " + counter);
    }
}

2. Lock接口

Java
// Lock使用
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private final Lock lock = new ReentrantLock();
    private int counter = 0;

    public void increment() {
        lock.lock();
        try {
            counter++;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        LockExample example = new LockExample();

        // 创建多个线程
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    example.increment();
                }
            });
            threads[i].start();
        }

        // 等待所有线程完成
        for (Thread thread : threads) {
            thread.join();
        }

        System.out.println("最终计数: " + example.counter);
    }
}

🏊 线程池

1. ExecutorService

Java
// 线程池使用
import java.util.concurrent.*;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建固定大小的线程池
        // ⚠️ 注意:Executors.newFixedThreadPool() 内部使用无界队列 (LinkedBlockingQueue),
        // 在高负载下任务会无限堆积,可能导致 OOM。生产环境建议直接使用 ThreadPoolExecutor
        // 并指定有界队列:new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
        //     new LinkedBlockingQueue<>(1000), new ThreadPoolExecutor.CallerRunsPolicy())
        ExecutorService executor = Executors.newFixedThreadPool(5);

        // 提交任务
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("任务" + taskId + "开始执行,线程: " + Thread.currentThread().getName());
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt(); // 恢复中断标志
                    e.printStackTrace();
                }
                System.out.println("任务" + taskId + "执行完成");
            });
        }

        // 关闭线程池
        executor.shutdown();

        try {
            // 等待所有任务完成
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                System.out.println("任务未在指定时间内完成");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("所有任务完成");
    }
}

2. Callable和Future

Java
// Callable和Future使用
import java.util.concurrent.*;

public class CallableFutureExample {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 提交Callable任务
        Future<Integer> future1 = executor.submit(() -> {
            Thread.sleep(2000);
            return 100;
        });

        Future<Integer> future2 = executor.submit(() -> {
            Thread.sleep(1000);
            return 200;
        });

        Future<Integer> future3 = executor.submit(() -> {
            Thread.sleep(3000);
            return 300;
        });

        // 获取结果
        System.out.println("任务1结果: " + future1.get());
        System.out.println("任务2结果: " + future2.get());
        System.out.println("任务3结果: " + future3.get());

        executor.shutdown();
    }
}

💡 最佳实践

1. 线程安全

Java
// 线程安全最佳实践
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadSafety {
    // ✅ 好的做法:使用线程安全的类
    private AtomicInteger counter = new AtomicInteger(0);

    public void increment() {
        counter.incrementAndGet();
    }

    // ❌ 不好的做法:使用非线程安全的操作
    private int unsafeCounter = 0;

    public void unsafeIncrement() {
        unsafeCounter++;  // 非线程安全
    }

    // ✅ 好的做法:使用并发集合
    private ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

    // ❌ 不好的做法:使用非线程安全的集合
    private HashMap<String, Integer> unsafeMap = new HashMap<>();
}

2. 性能优化

Java
// 多线程性能优化
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MultithreadingOptimization {
    // ✅ 好的做法:使用线程池
    private ExecutorService executor = Executors.newFixedThreadPool(10);

    public void processTasks(List<Runnable> tasks) {
        for (Runnable task : tasks) {
            executor.submit(task);
        }
    }

    // ❌ 不好的做法:为每个任务创建新线程
    public void processTasksUnsafe(List<Runnable> tasks) {
        for (Runnable task : tasks) {
            new Thread(task).start();  // 资源浪费
        }
    }

    // ✅ 好的做法:使用适当的锁粒度
    private final Lock fineLock = new ReentrantLock();

    public void fineGrainedLock() {
        fineLock.lock();
        try {
            // 只锁定必要的代码
        } finally {
            fineLock.unlock();
        }
    }
}

📝 练习题

基础题

  1. 如何创建线程?
  2. synchronized和Lock有什么区别?
  3. 什么是线程池?

进阶题

  1. 实现生产者消费者模式。
  2. 使用线程池提高性能。
  3. 实现线程安全的数据结构。

实践题

  1. 创建一个多线程下载器。
  2. 实现一个简单的任务调度器。
  3. 构建一个并发计算器。

📚 推荐阅读

🔗 下一章

网络编程 - 学习Java的网络编程。