Java-多线程_java 多线程

Java 的多线程(Multithreading)是实现并发编程的核心机制,允许程序同时执行多个任务,提高资源利用率和响应速度。Java 提供了强大的多线程支持,通过 java.lang.Thread 类和 java.util.concurrent 包实现。以下是对 Java 多线程的详细讲解,包括原理、实现方式、同步机制、线程池及最佳实践,帮助你彻底掌握 Java 多线程。


一、Java 多线程概述

1. 什么是多线程?

  • 线程:操作系统调度的最小单元,属于同一进程内的执行流。
  • 多线程:一个进程内多个线程并发执行,共享进程的内存空间(如堆、方法区),但每个线程有自己的栈。
  • 优势
  • 提高效率:利用多核 CPU 并行执行任务。
  • 快速响应:I/O 操作(如网络请求、文件读写)期间可切换线程。
  • 资源共享:线程间共享内存,通信成本低。

2. Java 中的线程

  • Java 线程基于操作系统的原生线程(非绿色线程),由 JVM 管理和调度。
  • Java 提供 Thread 类和 Runnable 接口作为线程基础,java.util.concurrent 包提供了高级并发工具。

3. 适用场景

  • CPU 密集型任务:如复杂计算,利用多核 CPU。
  • I/O 密集型任务:如网络请求、文件操作,线程可等待 I/O 时切换。
  • 并发任务:如服务器处理多个客户端请求。

二、创建线程的四种方式

1. 继承 Thread

  • 定义:继承 Thread 并重写 run() 方法。
  • 示例:
  public class MyThread extends Thread {
      @Override
      public void run() {
          System.out.println("线程 " + Thread.currentThread().getName() + " 运行");
      }

      public static void main(String[] args) {
          MyThread t1 = new MyThread();
          MyThread t2 = new MyThread();
          t1.start();
          t2.start();
      }
  }
  • 输出(顺序可能不同):
  线程 Thread-0 运行
  线程 Thread-1 运行
  • 特点:简单,但不能继承其他类。

2. 实现 Runnable 接口

  • 定义:实现 Runnable 接口的 run() 方法,将实例传递给 Thread
  • 示例:
  public class MyRunnable implements Runnable {
      @Override
      public void run() {
          System.out.println("线程 " + Thread.currentThread().getName() + " 运行");
      }

      public static void main(String[] args) {
          Runnable runnable = new MyRunnable();
          Thread t1 = new Thread(runnable);
          Thread t2 = new Thread(runnable);
          t1.start();
          t2.start();
      }
  }
  • 特点:灵活,可继承其他类,推荐使用。

3. 实现 Callable 接口

  • 定义:实现 Callable 接口的 call() 方法,返回结果并支持异常抛出,需结合 Future 获取结果。
  • 示例:
  import java.util.concurrent.Callable;
  import java.util.concurrent.FutureTask;

  public class MyCallable implements Callable<String> {
      @Override
      public String call() throws Exception {
          return "线程 " + Thread.currentThread().getName() + " 返回结果";
      }

      public static void main(String[] args) throws Exception {
          Callable<String> callable = new MyCallable();
          FutureTask<String> task = new FutureTask<>(callable);
          Thread t = new Thread(task);
          t.start();
          System.out.println(task.get()); // 等待结果
      }
  }
  • 输出
  线程 Thread-0 返回结果
  • 特点:支持返回值和异常处理,常用于需要结果的任务。

4. 使用线程池(ExecutorService)

  • 定义:通过 java.util.concurrent.ExecutorService 创建线程池,避免手动创建线程。
  • 示例:
  import java.util.concurrent.ExecutorService;
  import java.util.concurrent.Executors;

  public class ThreadPoolExample {
      public static void main(String[] args) {
          ExecutorService executor = Executors.newFixedThreadPool(2);
          executor.submit(() -> System.out.println("线程 " + Thread.currentThread().getName() + " 运行"));
          executor.submit(() -> System.out.println("线程 " + Thread.currentThread().getName() + " 运行"));
          executor.shutdown();
      }
  }
  • 特点:高效管理线程,适合大量任务。

三、线程状态

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

  1. NEW:线程创建但未调用 start()
  2. RUNNABLE:线程正在运行或就绪(等待 CPU)。
  3. BLOCKED:线程等待锁(如进入 synchronized 块)。
  4. WAITING:线程等待其他线程通知(如 wait()join())。
  5. TIMED_WAITING:限时等待(如 sleep(1000)wait(1000))。
  6. TERMINATED:线程执行完成或异常终止。

四、线程同步机制

多线程共享资源可能导致数据竞争(Race Condition)或线程安全问题,需使用同步机制。

1. synchronized 关键字

  • 作用:确保同一时间只有一个线程访问共享资源。
  • 方式
  • 同步方法
    java public synchronized void increment() { counter++; }
  • 同步块public void increment() { synchronized(this) { counter++; } }
  • 示例(计数器线程安全):
  public class Counter {
      private int counter = 0;

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

      public int getCounter() {
          return counter;
      }

      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("最终计数: " + counter.getCounter()); // 输出: 2000
      }
  }

2. Lock 接口

  • 定义:java.util.concurrent.locks.Lock 提供比 synchronized 更灵活的锁机制,如 ReentrantLock
  • 示例:
  import java.util.concurrent.locks.ReentrantLock;

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

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

      public int getCounter() {
          return counter;
      }

      public static void main(String[] args) throws InterruptedException {
          LockExample example = new LockExample();
          Runnable task = () -> {
              for (int i = 0; i < 1000; i++) {
                  example.increment();
              }
          };
          Thread t1 = new Thread(task);
          Thread t2 = new Thread(task);
          t1.start();
          t2.start();
          t1.join();
          t2.join();
          System.out.println("最终计数: " + example.getCounter()); // 输出: 2000
      }
  }
  • 优势:支持公平锁、超时锁、条件变量。

3. 其他同步工具

  • volatile:保证变量可见性,适合简单同步场景。
  volatile boolean running = true;
  • Condition:与 Lock 配合,实现线程间等待/通知。
  • Semaphore:控制同时访问资源的线程数。
  • CountDownLatch:等待多个线程完成。
  • CyclicBarrier:多个线程等待彼此到达某点。

五、线程池(Executor Framework)

线程池通过 java.util.concurrent.ExecutorService 管理线程,减少创建/销毁线程的开销。

1. 常见线程池

  • Executors.newFixedThreadPool(n):固定大小线程池。
  • Executors.newCachedThreadPool():动态大小线程池,适合短任务。
  • Executors.newSingleThreadExecutor():单线程执行。
  • Executors.newScheduledThreadPool(n):支持定时任务。

2. 示例(线程池)

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

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        for (int i = 1; i <= 5; i++) {
            int taskId = i;
            executor.submit(() -> {
                System.out.println("任务 " + taskId + " 由线程 " + Thread.currentThread().getName() + " 执行");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executor.shutdown();
    }
}
  • 输出(顺序可能不同):
  任务 1 由线程 pool-1-thread-1 执行
  任务 2 由线程 pool-1-thread-2 执行
  任务 3 由线程 pool-1-thread-3 执行
  任务 4 由线程 pool-1-thread-1 执行
  任务 5 由线程 pool-1-thread-2 执行

3. 线程池管理

  • 关闭线程池
  • shutdown():等待任务完成后再关闭。
  • shutdownNow():立即停止,尝试中断运行任务。
  • Future:获取任务结果或状态:
  Future<Integer> future = executor.submit(() -> 42);
  System.out.println(future.get()); // 输出: 42

六、线程安全问题

1. 数据竞争

  • 原因:多个线程同时修改共享资源。
  • 解决:使用 synchronizedLock 或线程安全集合(如 ConcurrentHashMap)。

2. 死锁

  • 原因:多个线程相互等待对方释放锁。
  • 解决
  • 按固定顺序获取锁。
  • 使用超时锁(如 lock.tryLock(1, TimeUnit.SECONDS))。
  • 工具检测:JVisualVM、JStack。

3. 线程安全集合

  • Collections.synchronizedXXX:同步包装器(如 synchronizedMap)。
  • ConcurrentHashMap:高效并发映射。
  • CopyOnWriteArrayList:适合读多写少场景。

七、最佳实践

  1. 选择合适的线程创建方式
  • 优先使用 RunnableCallable,避免继承 Thread
  • 大量任务使用线程池(ExecutorService)。
  1. 线程安全
  • 使用 synchronizedLock 保护共享资源。
  • 优先使用 java.util.concurrent 提供的线程安全类。
  1. 线程池优化
  • 根据任务类型选择线程池(固定大小、缓存、定时)。
  • 合理设置线程数(如 CPU 核心数 + 1)。
  1. 避免死锁
  • 按统一顺序加锁。
  • 使用 tryLock 避免无限等待。
  1. 异常处理
  • run()call() 中捕获异常,避免线程无声失败。
  1. 监控与调试
  • 使用 Thread.currentThread().getName() 跟踪线程。
  • 借助工具(如 JVisualVM)分析线程状态。
  1. 关闭资源
  • 确保线程池使用 shutdown()shutdownNow() 关闭。
  • 释放锁和其他资源。

八、常见问题与解决

  1. 问题:线程未结束,主线程退出
  • 解决:使用 join() 等待线程完成,或设为守护线程(setDaemon(true))。
  1. 问题:数据竞争导致结果错误
  • 解决:加锁或使用线程安全集合。
  1. 问题:死锁
  • 解决:检查锁顺序,使用超时机制,分析堆栈跟踪。
  1. 问题:线程池任务堆积
  • 解决:限制任务队列大小,优化任务处理逻辑。

九、总结

Java 多线程通过 ThreadRunnableCallable 和线程池实现并发,适用于 CPU 密集型和 I/O 密集型任务。synchronizedLock 确保线程安全,java.util.concurrent 提供高效并发工具(如 ExecutorServiceConcurrentHashMap)。遵循最佳实践(如使用线程池、避免死锁、优化线程数),可以编写高效、可靠的多线程程序。

如果需要更深入的示例(如线程池调优、并发集合使用、死锁调试)或与 ForkJoinPoolCompletableFuture 的对比,请告诉我!

类似文章

发表回复

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