关于Java多线程详解

简介: 本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。

💡 摘要:你是否曾为程序性能瓶颈而苦恼?是否想充分利用多核CPU的强大能力?是否被线程安全问题和死锁困扰?

别担心,多线程编程是Java中最重要的高级特性之一,它能显著提升程序性能,但同时也带来了复杂的并发问题。

本文将带你从多线程基础概念讲起,理解进程与线程的根本区别。然后深入线程的创建和管理,学习多种线程实现方式。

接着探索线程同步机制,掌握如何解决竞态条件和保证线程安全。最后通过实战案例展示多线程在性能优化、异步处理等场景的应用。从基础概念到高级特性,从内存模型到并发工具,让你全面掌握Java多线程编程。文末附常见问题和面试高频问题,助你写出高性能的并发程序。

一、多线程基础概念

1. 进程 vs 线程

根本区别

java

// 进程:程序的执行实例,拥有独立的内存空间

// 线程:进程内的执行单元,共享进程内存


public class ProcessVsThread {

   public static void main(String[] args) {

       // 启动多个进程(不同的Java程序)

       // 每个进程有独立的堆、栈、方法区

       

       // 在同一个进程内启动多个线程

       Thread thread1 = new Thread(new Task());

       Thread thread2 = new Thread(new Task());

       thread1.start(); // 共享同一堆内存

       thread2.start(); // 共享同一方法区

   }

}


class Task implements Runnable {

   public void run() {

       // 线程执行的任务

   }

}

对比表格

特性 进程 线程
内存空间 独立 共享
通信方式 管道、Socket、文件 共享变量、队列
创建开销
上下文切换
独立性 相互隔离 相互影响

2. 为什么需要多线程?

多线程的优势

java

public class MultiThreadBenefits {

   public static void main(String[] args) {

       // 1. 提高CPU利用率(多核处理器)

       int processorCores = Runtime.getRuntime().availableProcessors();

       System.out.println("可用处理器核心: " + processorCores);

       

       // 2. 提高响应性(GUI应用)

       // 主线程处理UI,后台线程处理耗时操作

       

       // 3. 简化建模(模拟现实世界)

       // 每个线程可以代表一个独立的活动实体

       

       // 4. 异步处理(非阻塞操作)

       // I/O操作时,CPU可以执行其他任务

   }

}

二、线程创建与管理

1. 创建线程的三种方式

方式1:继承Thread类

java

class MyThread extends Thread {

   @Override

   public void run() {

       System.out.println("线程运行: " + Thread.currentThread().getName());

       // 执行具体任务

   }

}


// 使用

MyThread thread = new MyThread();

thread.start(); // 启动线程

方式2:实现Runnable接口(推荐)

java

class MyTask implements Runnable {

   @Override

   public void run() {

       System.out.println("任务执行: " + Thread.currentThread().getName());

       // 执行具体任务

   }

}


// 使用

Thread thread = new Thread(new MyTask());

thread.start();


// 或者使用线程池

ExecutorService executor = Executors.newFixedThreadPool(5);

executor.execute(new MyTask());

方式3:实现Callable接口(带返回值)

java

class ComputeTask implements Callable<Integer> {

   private final int number;

   

   public ComputeTask(int number) {

       this.number = number;

   }

   

   @Override

   public Integer call() throws Exception {

       // 模拟耗时计算

       Thread.sleep(1000);

       return number * number;

   }

}


// 使用

ExecutorService executor = Executors.newFixedThreadPool(3);

Future<Integer> future = executor.submit(new ComputeTask(5));


// 获取结果(阻塞直到计算完成)

Integer result = future.get();

System.out.println("计算结果: " + result);

2. 线程生命周期

线程状态转换

java

public class ThreadStates {

   public static void main(String[] args) throws Exception {

       Thread thread = new Thread(() -> {

           try {

               Thread.sleep(2000); // TIMED_WAITING

               synchronized (ThreadStates.class) {

                   ThreadStates.class.wait(); // WAITING

               }

           } catch (InterruptedException e) {

               e.printStackTrace();

           }

       });

       

       System.out.println("新建状态: " + thread.getState()); // NEW

       

       thread.start();

       System.out.println("启动后状态: " + thread.getState()); // RUNNABLE

       

       Thread.sleep(100);

       System.out.println("运行中状态: " + thread.getState()); // TIMED_WAITING

       

       Thread.sleep(3000);

       synchronized (ThreadStates.class) {

           ThreadStates.class.notify(); // 唤醒线程

       }

       

       Thread.sleep(100);

       System.out.println("最终状态: " + thread.getState()); // TERMINATED

   }

}

三、线程同步与通信

1. synchronized 关键字

同步方法

java

class Counter {

   private int count = 0;

   

   // 同步方法

   public synchronized void increment() {

       count++;

   }

   

   // 同步静态方法

   public static synchronized void staticMethod() {

       // 使用Class对象作为锁

   }

   

   // 同步代码块

   public void incrementWithBlock() {

       synchronized (this) { // 使用当前对象作为锁

           count++;

       }

   }

}

2. volatile 关键字

保证可见性

java

class SharedData {

   private volatile boolean running = true;

   

   public void stop() {

       running = false; // 修改立即对其他线程可见

   }

   

   public void run() {

       while (running) {

           // 执行任务

       }

   }

}

3. 锁对象(Lock)

显式锁使用

java

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;


class ThreadSafeCounter {

   private int count = 0;

   private final Lock lock = new ReentrantLock();

   

   public void increment() {

       lock.lock(); // 获取锁

       try {

           count++;

       } finally {

           lock.unlock(); // 确保释放锁

       }

   }

   

   // 尝试获取锁

   public boolean tryIncrement() {

       if (lock.tryLock()) {

           try {

               count++;

               return true;

           } finally {

               lock.unlock();

           }

       }

       return false;

   }

}

4. 线程通信

wait/notify机制

java

class MessageQueue {

   private String message;

   private boolean empty = true;

   

   public synchronized String take() throws InterruptedException {

       while (empty) {

           wait(); // 释放锁并等待

       }

       empty = true;

       notifyAll(); // 通知生产者

       return message;

   }

   

   public synchronized void put(String message) throws InterruptedException {

       while (!empty) {

           wait(); // 释放锁并等待

       }

       empty = false;

       this.message = message;

       notifyAll(); // 通知消费者

   }

}

四、Java并发工具类

1. CountDownLatch

等待多个任务完成

java

public class CountDownLatchDemo {

   public static void main(String[] args) throws InterruptedException {

       int taskCount = 5;

       CountDownLatch latch = new CountDownLatch(taskCount);

       

       for (int i = 0; i < taskCount; i++) {

           new Thread(() -> {

               try {

                   // 执行任务

                   Thread.sleep((long) (Math.random() * 1000));

                   System.out.println("任务完成: " + Thread.currentThread().getName());

               } catch (InterruptedException e) {

                   e.printStackTrace();

               } finally {

                   latch.countDown(); // 计数减1

               }

           }).start();

       }

       

       latch.await(); // 等待所有任务完成

       System.out.println("所有任务已完成,继续主线程");

   }

}

2. CyclicBarrier

多线程同步点

java

public class CyclicBarrierDemo {

   public static void main(String[] args) {

       int threadCount = 3;

       CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {

           System.out.println("所有线程已到达屏障点");

       });

       

       for (int i = 0; i < threadCount; i++) {

           new Thread(() -> {

               try {

                   System.out.println("线程准备: " + Thread.currentThread().getName());

                   Thread.sleep((long) (Math.random() * 1000));

                   barrier.await(); // 等待其他线程

                   System.out.println("线程继续: " + Thread.currentThread().getName());

               } catch (Exception e) {

                   e.printStackTrace();

               }

           }).start();

       }

   }

}

3. Semaphore

控制资源访问

java

public class SemaphoreDemo {

   public static void main(String[] args) {

       // 允许3个线程同时访问

       Semaphore semaphore = new Semaphore(3);

       

       for (int i = 0; i < 10; i++) {

           new Thread(() -> {

               try {

                   semaphore.acquire(); // 获取许可

                   System.out.println("线程开始访问: " + Thread.currentThread().getName());

                   Thread.sleep(2000); // 模拟资源访问

                   System.out.println("线程结束访问: " + Thread.currentThread().getName());

               } catch (InterruptedException e) {

                   e.printStackTrace();

               } finally {

                   semaphore.release(); // 释放许可

               }

           }).start();

       }

   }

}

五、线程池管理

1. Executor框架

线程池创建

java

public class ThreadPoolDemo {

   public static void main(String[] args) {

       // 1. 固定大小线程池

       ExecutorService fixedPool = Executors.newFixedThreadPool(5);

       

       // 2. 缓存线程池(自动扩容)

       ExecutorService cachedPool = Executors.newCachedThreadPool();

       

       // 3. 单线程线程池(顺序执行)

       ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();

       

       // 4. 定时任务线程池

       ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3);

       

       // 提交任务

       for (int i = 0; i < 10; i++) {

           fixedPool.execute(() -> {

               System.out.println("任务执行: " + Thread.currentThread().getName());

           });

       }

       

       // 优雅关闭

       fixedPool.shutdown();

       try {

           if (!fixedPool.awaitTermination(60, TimeUnit.SECONDS)) {

               fixedPool.shutdownNow();

           }

       } catch (InterruptedException e) {

           fixedPool.shutdownNow();

       }

   }

}

2. 自定义线程池

ThreadPoolExecutor配置

java

public class CustomThreadPool {

   public static void main(String[] args) {

       int corePoolSize = 5;

       int maxPoolSize = 10;

       long keepAliveTime = 60L;

       BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100);

       

       ThreadPoolExecutor executor = new ThreadPoolExecutor(

           corePoolSize,

           maxPoolSize,

           keepAliveTime,

           TimeUnit.SECONDS,

           workQueue,

           new ThreadFactory() {

               private final AtomicInteger count = new AtomicInteger(1);

               @Override

               public Thread newThread(Runnable r) {

                   return new Thread(r, "custom-pool-" + count.getAndIncrement());

               }

           },

           new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略

       );

       

       // 使用线程池

       for (int i = 0; i < 20; i++) {

           executor.execute(new Task(i));

       }

       

       executor.shutdown();

   }

}

六、线程安全集合

1. ConcurrentHashMap

并发Map实现

java

public class ConcurrentHashMapDemo {

   public static void main(String[] args) {

       ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

       

       // 多线程安全操作

       ExecutorService executor = Executors.newFixedThreadPool(10);

       for (int i = 0; i < 1000; i++) {

           final int index = i;

           executor.execute(() -> {

               map.put("key" + index, index);

           });

       }

       

       executor.shutdown();

       

       // 原子操作

       map.compute("key1", (k, v) -> v == null ? 1 : v + 1);

       map.merge("key2", 1, Integer::sum);

   }

}

2. CopyOnWriteArrayList

读多写少场景

java

public class CopyOnWriteDemo {

   public static void main(String[] args) {

       CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

       

       // 写操作(复制整个数组)

       list.add("item1");

       list.add("item2");

       

       // 读操作(无锁,高性能)

       for (String item : list) {

           System.out.println(item);

       }

       

       // 适合监听器列表等读多写少的场景

   }

}

七、实战应用案例

1. 生产者-消费者模式

使用BlockingQueue实现

java

public class ProducerConsumerDemo {

   public static void main(String[] args) {

       BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

       

       // 生产者

       Runnable producer = () -> {

           try {

               int value = 0;

               while (true) {

                   queue.put(value);

                   System.out.println("生产: " + value);

                   value++;

                   Thread.sleep(100);

               }

           } catch (InterruptedException e) {

               e.printStackTrace();

           }

       };

       

       // 消费者

       Runnable consumer = () -> {

           try {

               while (true) {

                   Integer value = queue.take();

                   System.out.println("消费: " + value);

                   Thread.sleep(200);

               }

           } catch (InterruptedException e) {

               e.printStackTrace();

           }

       };

       

       // 启动生产者和消费者

       new Thread(producer).start();

       new Thread(consumer).start();

       new Thread(consumer).start();

   }

}

2. 并行计算

使用ForkJoinPool

java

public class ParallelCompute {

   public static void main(String[] args) {

       ForkJoinPool pool = new ForkJoinPool();

       long[] numbers = new long[10000];

       

       // 初始化数组

       for (int i = 0; i < numbers.length; i++) {

           numbers[i] = i + 1;

       }

       

       // 提交任务

       Long result = pool.invoke(new SumTask(numbers, 0, numbers.length));

       System.out.println("计算结果: " + result);

   }

}


class SumTask extends RecursiveTask<Long> {

   private final long[] numbers;

   private final int start;

   private final int end;

   private static final int THRESHOLD = 1000;

   

   public SumTask(long[] numbers, int start, int end) {

       this.numbers = numbers;

       this.start = start;

       this.end = end;

   }

   

   @Override

   protected Long compute() {

       int length = end - start;

       if (length <= THRESHOLD) {

           // 直接计算

           long sum = 0;

           for (int i = start; i < end; i++) {

               sum += numbers[i];

           }

           return sum;

       } else {

           // 拆分任务

           int middle = start + length / 2;

           SumTask leftTask = new SumTask(numbers, start, middle);

           SumTask rightTask = new SumTask(numbers, middle, end);

           

           leftTask.fork(); // 异步执行左任务

           Long rightResult = rightTask.compute(); // 同步执行右任务

           Long leftResult = leftTask.join(); // 等待左任务完成

           

           return leftResult + rightResult;

       }

   }

}

八、常见问题与解决方案

1. 死锁预防

死锁示例与解决

java

public class DeadlockPrevention {

   private final Object lock1 = new Object();

   private final Object lock2 = new Object();

   

   public void method1() {

       synchronized (lock1) {

           System.out.println("获取lock1");

           try { Thread.sleep(100); } catch (InterruptedException e) {}

           synchronized (lock2) { // 可能死锁

               System.out.println("获取lock2");

           }

       }

   }

   

   public void method2() {

       synchronized (lock2) {

           System.out.println("获取lock2");

           try { Thread.sleep(100); } catch (InterruptedException e) {}

           synchronized (lock1) { // 可能死锁

               System.out.println("获取lock1");

           }

       }

   }

   

   // 解决方案:按顺序获取锁

   public void safeMethod1() {

       synchronized (lock1) {

           synchronized (lock2) {

               // 安全操作

           }

       }

   }

   

   public void safeMethod2() {

       synchronized (lock1) {

           synchronized (lock2) {

               // 安全操作

           }

       }

   }

}

2. 线程池调优

性能优化建议

java

public class ThreadPoolTuning {

   public static void main(String[] args) {

       // CPU密集型任务

       int cpuCores = Runtime.getRuntime().availableProcessors();

       ExecutorService cpuBoundPool = Executors.newFixedThreadPool(cpuCores);

       

       // I/O密集型任务

       ExecutorService ioBoundPool = Executors.newCachedThreadPool();

       

       // 混合型任务

       ThreadPoolExecutor mixedPool = new ThreadPoolExecutor(

           cpuCores * 2,      // 核心线程数

           cpuCores * 4,      // 最大线程数

           60L, TimeUnit.SECONDS,

           new LinkedBlockingQueue<>(1000)

       );

   }

}

九、总结:多线程最佳实践

1. 使用建议

  • ✅ 优先使用线程池而不是直接创建线程
  • ✅ 使用并发集合而不是同步的集合
  • ✅ 尽量使用不可变对象
  • ✅ 明确锁的范围,减小同步代码块
  • ✅ 使用高级并发工具类

2. 避免的陷阱

  • 🔴 避免死锁(按顺序获取锁)
  • 🔴 避免过度同步(性能问题)
  • 🔴 避免使用stop()、suspend()等过时方法
  • 🔴 避免忙等待(使用wait/notify)

十、面试高频问题

❓1. synchronized和ReentrantLock有什么区别?

:synchronized是关键字,JVM实现;ReentrantLock是类,API更丰富(可中断、超时、公平锁等)。

❓2. volatile关键字有什么作用?

:保证可见性(修改立即对其他线程可见)和禁止指令重排序,但不保证原子性。

❓3. 什么是线程安全?如何实现?

:多线程环境下程序的行为符合预期。实现方式:同步、不可变对象、线程局部变量、并发集合等。

❓4. 线程池的核心参数有哪些?

:核心线程数、最大线程数、空闲时间、工作队列、线程工厂、拒绝策略。

❓5. 如何避免死锁?

:按固定顺序获取锁、使用尝试获取锁机制、设置超时时间、使用死锁检测工具。

相关文章
|
1月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
135 1
|
1月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
156 1
|
2月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
134 0
|
2月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
217 16
|
3月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践
|
4月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
349 83
|
4月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
198 0
|
4月前
|
存储 Java 调度
Java虚拟线程:轻量级并发的革命性突破
Java虚拟线程:轻量级并发的革命性突破
326 83