Java 多线程编程

Java 多线程编程是 Java 平台的核心特性之一,用于实现并发执行,提高程序的性能和响应能力。它广泛应用于服务器开发、GUI 应用、游戏开发等场景。以下是一个简明实用的 Java 多线程编程入门教程,涵盖核心概念、实现方式、线程安全、常见工具和示例代码,适合初学者快速上手。


1. 多线程简介

  • 定义:多线程是指在同一程序中同时运行多个线程(Thread),每个线程执行不同的任务。Java 提供了内置支持,通过 java.lang.Thread 类和 java.util.concurrent 包实现。
  • 优势
  • 提高 CPU 利用率(并行执行任务)。
  • 提升程序响应速度(如 GUI 不卡顿)。
  • 简化异步任务处理。
  • 挑战
  • 线程安全:多线程访问共享资源可能导致数据不一致。
  • 线程同步:需要协调线程以避免竞争条件。
  • 性能开销:线程创建和切换有一定成本。

2. 创建线程的两种方式

Java 提供了两种主要方式创建线程:

2.1 继承 Thread 类

  • 定义一个类继承 Thread,重写 run() 方法。
  • 调用 start() 方法启动线程。
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running: " + Thread.currentThread().getName());
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // 启动线程
    }
}
  • 说明start() 调用后,JVM 创建新线程并执行 run() 方法。直接调用 run() 不会创建新线程。

2.2 实现 Runnable 接口

  • 定义一个类实现 Runnable 接口,实现 run() 方法。
  • Runnable 对象传递给 Thread 构造器。
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Thread running: " + Thread.currentThread().getName());
    }
}

public class Main {
    public static void main(String[] args) {
        Runnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}
  • 优点Runnable 更灵活,支持接口实现,可继承其他类。

推荐:优先使用 Runnable,因为它更符合面向接口编程,且便于与线程池等高级工具结合。


3. 线程的基本操作

3.1 线程状态

线程有以下状态(Thread.State 枚举):

  • NEW:创建但未启动。
  • RUNNABLE:正在运行或等待 CPU。
  • BLOCKED:等待锁(同步块)。
  • WAITING:等待其他线程通知(如 wait()join())。
  • TIMED_WAITING:有限时间等待(如 sleep()wait(timeout))。
  • TERMINATED:线程执行完成。

3.2 常用方法

  • start():启动线程。
  • run():定义线程执行的任务。
  • sleep(long millis):让线程休眠指定毫秒,暂停执行。
  • join():等待线程执行完成。
  • setPriority(int priority):设置线程优先级(1-10,默认为 5)。
  • interrupt():中断线程(需配合处理中断状态)。
  • Thread.currentThread():获取当前线程。

示例:休眠和优先级

public class Main {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                System.out.println("T1: " + i);
                try {
                    Thread.sleep(1000); // 休眠 1 秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.setPriority(Thread.MAX_PRIORITY); // 设置最高优先级
        t1.start();
    }
}

4. 线程同步与线程安全

多线程访问共享资源可能导致数据不一致,因此需要同步机制。

4.1 synchronized 关键字

  • 作用:确保同一时间只有一个线程访问同步代码块或方法。
  • 方式
  • 同步方法:在方法上加 synchronized
  • 同步块:使用 synchronized(obj) 指定锁对象。

示例:计数器同步

class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Count: " + counter.getCount()); // 输出 2000
    }
}
  • 说明synchronized 确保 increment() 方法线程安全。

4.2 Lock 接口

Java 提供了 java.util.concurrent.locks.Lock(如 ReentrantLock),比 synchronized 更灵活,支持超时和可中断锁。

示例:使用 ReentrantLock

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

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

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

    public int getCount() {
        return count;
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Count: " + counter.getCount()); // 输出 2000
    }
}

5. 线程通信

线程之间需要协作,常用机制包括:

  • wait()notify() / notifyAll()
  • synchronized 块中使用,线程等待或唤醒。
  • Condition:与 Lock 结合使用,类似 wait/notify

示例:生产者-消费者

import java.util.LinkedList;
import java.util.Queue;

class ProducerConsumer {
    private Queue<Integer> queue = new LinkedList<>();
    private final int MAX_SIZE = 5;

    public synchronized void produce(int value) throws InterruptedException {
        while (queue.size() == MAX_SIZE) {
            wait(); // 队列满,等待
        }
        queue.add(value);
        System.out.println("Produced: " + value);
        notifyAll(); // 唤醒消费者
    }

    public synchronized int consume() throws InterruptedException {
        while (queue.isEmpty()) {
            wait(); // 队列空,等待
        }
        int value = queue.poll();
        System.out.println("Consumed: " + value);
        notifyAll(); // 唤醒生产者
        return value;
    }
}

public class Main {
    public static void main(String[] args) {
        ProducerConsumer pc = new ProducerConsumer();

        Thread producer = new Thread(() -> {
            try {
                for (int i = 1; i <= 5; i++) {
                    pc.produce(i);
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread consumer = new Thread(() -> {
            try {
                for (int i = 1; i <= 5; i++) {
                    pc.consume();
                    Thread.sleep(150);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        producer.start();
        consumer.start();
    }
}
  • 输出示例
Produced: 1
Consumed: 1
Produced: 2
Consumed: 2
...

6. 线程池

创建大量线程会消耗资源,Java 提供了 java.util.concurrent.ExecutorService 来管理线程池。

6.1 创建线程池

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

public class Main {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2); // 固定 2 个线程

        for (int i = 1; i <= 5; i++) {
            int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " running in " + Thread.currentThread().getName());
            });
        }

        executor.shutdown(); // 关闭线程池
    }
}
  • 说明
  • Executors.newFixedThreadPool(n):创建固定大小的线程池。
  • submit():提交任务(支持 RunnableCallable)。
  • shutdown():等待任务完成后关闭线程池。

6.2 常见线程池

  • Executors.newFixedThreadPool(int n):固定线程数。
  • Executors.newCachedThreadPool():动态调整线程数。
  • Executors.newSingleThreadExecutor():单线程执行。
  • Executors.newScheduledThreadPool(int n):支持定时任务。

7. 并发工具(java.util.concurrent)

Java 提供了丰富的并发工具:

  • CountDownLatch:等待多个线程完成。
import java.util.concurrent.CountDownLatch;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);

        Runnable task = () -> {
            System.out.println(Thread.currentThread().getName() + " finished");
            latch.countDown();
        };

        new Thread(task).start();
        new Thread(task).start();
        new Thread(task).start();

        latch.await(); // 等待 3 个线程完成
        System.out.println("All tasks done");
    }
}
  • CyclicBarrier:多个线程等待彼此到达某点。
  • Semaphore:控制同时访问资源的线程数。
  • ConcurrentHashMap:线程安全的哈希表。
  • BlockingQueue:线程安全的队列(如 ArrayBlockingQueue)。

8. 注意事项

  • 线程安全:共享资源需使用 synchronized、锁或并发集合。
  • 死锁:避免多个线程互相等待锁(使用锁排序或超时机制)。
  • 性能:线程池优于频繁创建线程,减少上下文切换。
  • 中断处理:捕获 InterruptedException,正确处理线程中断。
  • 现代替代:Java 8+ 的 CompletableFutureparallelStream 可简化异步编程。

9. 应用场景

  • Web 服务器:处理多个客户端请求(如 Tomcat)。
  • 任务调度:定时任务或后台处理。
  • GUI 应用:异步更新界面,避免卡顿。
  • 数据处理:并行处理大数据(如文件解析、图像处理)。

10. 总结

  • 核心概念:线程创建(ThreadRunnable)、同步(synchronizedLock)、线程池(ExecutorService)、并发工具。
  • 推荐实践
  • 使用 RunnableCallable 而非继承 Thread
  • 优先使用线程池管理线程。
  • 确保线程安全,合理选择同步机制。
  • 学习资源
  • 《Java Concurrency in Practice》(经典书籍)。
  • Oracle Java 并发教程(https://docs.oracle.com/javase/tutorial/essential/concurrency/)。
  • 实践:实现生产者-消费者、线程池任务调度等。

如果你需要更深入的某部分(如线程池优化、死锁调试、CompletableFuture 使用)或具体场景的代码示例,请告诉我!

类似文章

发表回复

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