在计算机性能日益提升的今天,单核处理器早已无法满足复杂应用的需求。现代计算机普遍配备多核 CPU,而多线程正是充分利用多核资源、提升程序效率的核心技术。

想象一下:如果一个文件下载器只能单线程工作,那么在下载文件时你就无法进行暂停、取消等操作;如果一个 Web 服务器只能处理一个请求,那么在高并发场景下会导致大量用户等待。多线程让程序能够 "同时" 执行多个任务,显著提升了系统的响应速度和资源利用率。

Java 作为一门成熟的编程语言,从设计之初就内置了完善的多线程支持。本文将带你全面了解 Java 多线程编程,从基础概念到实战技巧,助你掌握这一核心技术。

一.多线程基础概念

1.进程与线程的区别

  • 进程:操作系统进行资源分配的基本单位,每个进程拥有独立的内存空间、文件描述符等资源
  • 线程:进程内的执行单元,共享进程的资源,是 CPU 调度的基本单位。

一个进程可以包含多个线程,线程间的切换成本比进程切换成本低得多

2.并发与并行

  • 并发:多个线程在同一 CPU 上交替执行,宏观上看起来 "同时" 运行。
  • 并行:多个线程在不同 CPU 核心上真正同时执行。

Java 多线程编程既支持并发也支持并行,具体取决于硬件和 JVM 调度

3.线程的生命周期

一张图梳理出现成的生命周期:

二.创建线程的三种方式

1.继承Thread类
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("Thread " + Thread.currentThread().getId() + ": " + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        
        thread1.start();  // 启动线程
        thread2.start();
    }
}
2.实现Runnable接口
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("Thread " + Thread.currentThread().getId() + ": " + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyRunnable());
        Thread thread2 = new Thread(new MyRunnable());
        
        thread1.start();
        thread2.start();
    }
}
3.实现Callable接口
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 10; i++) {
            sum += i;
            Thread.sleep(50);
        }
        return sum;
    }
    
    public static void main(String[] args) {
        MyCallable callable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        
        Thread thread = new Thread(futureTask);
        thread.start();
        
        try {
            // 获取线程执行结果,会阻塞直到结果返回
            int result = futureTask.get();
            System.out.println("计算结果: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

三.线程同步机制

多线程共享资源时可能导致数据不一致问题,需要同步机制保证线程安全。

1.synchronized

// 同步方法
public synchronized void synchronizedMethod() {
    // 线程安全的操作
}

// 同步代码块
public void someMethod() {
    synchronized (this) {
        // 线程安全的操作
    }
}

2.ReentrantLock

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

public class LockExample {
    private int count = 0;
    private Lock lock = new ReentrantLock();
    
    public void increment() {
        lock.lock();  // 获取锁
        try {
            count++;  // 临界区操作
        } finally {
            lock.unlock();  // 确保释放锁
        }
    }
    
    public int getCount() {
        return count;
    }
    
    public static void main(String[] args) throws InterruptedException {
        LockExample example = new LockExample();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });
        
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });
        
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        
        System.out.println("最终结果: " + example.getCount());  // 应该是2000
    }
}

四.线程通信机制

常见的通信方式有以下几种:

1.wait(),notify(),notifyAll()

synchronized (obj) {
    while (条件不满足) {
        obj.wait();  // 释放锁并等待
    }
    // 执行操作
    obj.notify();  // 唤醒一个等待线程
    // 或 obj.notifyAll(); 唤醒所有等待线程
}

2.BlockingQueue实现 生产者-消费者模式

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class ProducerConsumer {
    private static final int QUEUE_CAPACITY = 5;
    private static BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
    
    // 生产者
    static class Producer implements Runnable {
        @Override
        public void run() {
            try {
                int value = 0;
                while (true) {
                    queue.put(value);
                    System.out.println("生产者生产: " + value + ", 队列大小: " + queue.size());
                    value++;
                    Thread.sleep(1000);  // 模拟生产耗时
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    // 消费者
    static class Consumer implements Runnable {
        @Override
        public void run() {
            try {
                while (true) {
                    int value = queue.take();
                    System.out.println("消费者消费: " + value + ", 队列大小: " + queue.size());
                    Thread.sleep(2000);  // 模拟消费耗时
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    public static void main(String[] args) {
        Thread producerThread = new Thread(new Producer());
        Thread consumerThread = new Thread(new Consumer());
        
        producerThread.start();
        consumerThread.start();
    }
}

五.线程池

线程池的执行流程如下

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 executor = Executors.newFixedThreadPool(3);
        
        // 提交5个任务
        for (int i = 0; i < 5; i++) {
            final int taskId = i;
            executor.submit(() -> {
                try {
                    System.out.println("任务 " + taskId + " 由线程 " + 
                                       Thread.currentThread().getId() + " 执行");
                    Thread.sleep(1000);  // 模拟任务执行时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        
        // 关闭线程池
        executor.shutdown();
        try {
            // 等待所有任务完成
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
                // 超时则强制关闭
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
    }
}

结语

Java 多线程编程是提升应用性能的重要手段,但也带来了复杂性。掌握多线程的创建方式、同步机制、通信方式和线程池的使用,能够帮助我们编写出高效、安全的并发程序。

多线程编程的核心在于理解线程的生命周期和如何协调多个线程的工作。在实际开发中,应根据具体场景选择合适的并发工具和模式,同时注意避免线程安全问题和性能陷阱。

希望本文能为你打开 Java 多线程编程的大门,在实践中不断积累经验,编写出更优秀的并发程序。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐