Java 多线程编程:从基础实现到高级应用

VibeCoding·九月创作之星挑战赛 10w+人浏览 1.3k人参与

多线程是 Java 并发编程的核心,它允许程序同时执行多个任务,显著提升程序效率。本文将系统梳理 Java 多线程的实现方式、核心 API、线程安全及经典协作模式,帮助读者全面掌握多线程编程技巧。

一、多线程基础概念

1. 线程与进程

  • 进程:程序的一次执行过程,是操作系统资源分配的基本单位
  • 线程:进程内的执行单元,是 CPU 调度的最小单位,一个进程可包含多个线程
  • 多线程:单个进程中同时运行多个线程,共享进程资源

2. 并发与并行

  • 并发:多个线程在单个 CPU 上交替执行(宏观同时,微观交替)
  • 并行:多个线程在多个 CPU 上同时执行(真正意义上的同时)

二、多线程的三种实现方式

1. 继承 Thread 类

通过继承Thread类并重写run()方法实现多线程:

// 定义线程类
class MyThread1 extends Thread {
    // 构造方法指定线程名
    public MyThread1(String name) {
        super(name);
    }
    
    // 重写run()方法,定义线程执行逻辑
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "启动了");
    }
}

// 使用线程
MyThread1 thread1 = new MyThread1("线程1");
thread1.start(); // 启动线程(底层调用run())

特点:实现简单,但受限于单继承机制,灵活性较低。

2. 实现 Runnable 接口

实现Runnable接口的run()方法,再通过Thread包装:

// 定义任务类
class MyThread2 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "启动了");
    }
}

// 使用线程
MyThread2 task = new MyThread2();
Thread thread2 = new Thread(task, "线程2"); // 包装任务并指定线程名
thread2.start();

特点:避免单继承限制,适合多线程共享同一个任务对象的场景。

3. 实现 Callable 接口(带返回值)

通过Callable接口实现有返回值的多线程,需配合FutureTask使用:

// 定义带返回值的任务
class MyThread3 implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName() + "启动了");
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            sum += i;
        }
        return sum; // 返回计算结果
    }
}

// 使用线程
MyThread3 callableTask = new MyThread3();
FutureTask<Integer> futureTask = new FutureTask<>(callableTask); // 管理结果
Thread thread3 = new Thread(futureTask, "线程3");
thread3.start();
Integer result = futureTask.get(); // 获取线程执行结果
System.out.println("计算结果:" + result);

特点:可获取线程执行结果,适合需要返回值的场景,但实现相对复杂。

三、Thread 类核心方法

方法名功能描述
String getName()获取线程名称
void setName(String name)设置线程名称
static Thread currentThread()获取当前执行的线程对象
static void sleep(long millis)让线程休眠指定毫秒数
void setPriority(int priority)设置线程优先级(1-10,默认 5)
int getPriority()获取线程优先级
void setDaemon(boolean on)设置为守护线程(后台线程)
boolean isDaemon()判断是否为守护线程
static void yield()线程礼让(让出 CPU 执行权)
void join()线程插队(让调用线程先执行完)

四、线程安全问题与解决方案

当多个线程共享资源时,可能出现数据不一致的安全问题(如多窗口售票超卖)。解决方式主要有:

1. 同步代码块

通过synchronized关键字锁定共享资源:

synchronized (锁对象) {
    // 操作共享资源的代码
}

要求:多个线程必须使用同一个锁对象。

2. 同步方法

synchronized关键字加到方法上:

// 非静态同步方法(锁对象为this)
public synchronized void sellTicket() {
    // 售票逻辑
}

// 静态同步方法(锁对象为类的字节码文件)
public static synchronized void sellTicket() {
    // 售票逻辑
}

3. Lock 锁(JDK 5+)

更灵活的锁机制,需手动获取和释放锁:

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

Lock lock = new ReentrantLock(); // 创建锁对象

try {
    lock.lock(); // 获取锁
    // 操作共享资源的代码
} finally {
    lock.unlock(); // 释放锁(确保锁一定会被释放)
}

五、死锁问题

死锁是多线程因争夺资源而形成的僵局,例如:

  • 线程 A 持有资源 1,等待资源 2
  • 线程 B 持有资源 2,等待资源 1

避免方式

  • 按顺序获取资源
  • 限时获取资源
  • 减少锁的嵌套使用

六、生产者消费者模式(线程协作)

这是多线程协作的经典模式,通过等待唤醒机制实现生产与消费的平衡。

1. 基于等待唤醒机制的实现

// 共享资源类
class Desk {
    public static int foodFlag = 0; // 0:无食物 1:有食物
    public static int count = 10;   // 总数量
    public static Object lock = new Object(); // 锁对象
}

// 生产者线程
class Producer extends Thread {
    @Override
    public void run() {
        while (true) {
            synchronized (Desk.lock) {
                if (Desk.count == 0) break; // 生产完毕
                if (Desk.foodFlag == 1) {
                    try {
                        Desk.lock.wait(); // 有食物则等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println("厨师制作了食物");
                    Desk.foodFlag = 1;
                    Desk.lock.notifyAll(); // 唤醒消费者
                }
            }
        }
    }
}

// 消费者线程
class Consumer extends Thread {
    @Override
    public void run() {
        while (true) {
            synchronized (Desk.lock) {
                if (Desk.count == 0) break; // 消费完毕
                if (Desk.foodFlag == 0) {
                    try {
                        Desk.lock.wait(); // 无食物则等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    Desk.count--;
                    System.out.println("消费者吃了食物,还剩" + Desk.count + "个");
                    Desk.foodFlag = 0;
                    Desk.lock.notifyAll(); // 唤醒生产者
                }
            }
        }
    }
}

2. 基于阻塞队列的实现(JDK 5+)

使用BlockingQueue简化协作,当队列空时消费者阻塞,队列满时生产者阻塞:

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

// 生产者
class Producer implements Runnable {
    private BlockingQueue<String> queue;
    // 构造方法注入队列
    public Producer(BlockingQueue<String> queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        try {
            queue.put("食物"); // 放入元素,满则阻塞
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

// 消费者
class Consumer implements Runnable {
    private BlockingQueue<String> queue;
    // 构造方法注入队列
    public Consumer(BlockingQueue<String> queue) {
        this.queue = queue;
    }
    @Override
    public void run() {
        try {
            String food = queue.take(); // 取出元素,空则阻塞
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

// 使用
BlockingQueue<String> queue = new ArrayBlockingQueue<>(1); // 容量为1的队列
new Thread(new Producer(queue)).start();
new Thread(new Consumer(queue)).start();

七、线程的生命周期

Java 线程有 6 种状态,定义在Thread.State枚举中:

  1. NEW:新建状态(未启动)
  2. RUNNABLE:运行状态(就绪或正在运行)
  3. BLOCKED:阻塞状态(等待锁)
  4. WAITING:无限等待状态(需被唤醒)
  5. TIMED_WAITING:计时等待状态(超时自动唤醒)
  6. TERMINATED:终止状态(已执行完毕)

八、总结

多线程是 Java 开发中提升程序性能的关键技术,掌握其实现方式、线程安全控制及协作模式至关重要。实际开发中需注意:

  • 根据场景选择合适的线程实现方式(无返回值用 Runnable,有返回值用 Callable)
  • 优先使用 Lock 锁(灵活性更高)处理线程安全问题
  • 避免死锁,合理设计资源竞争逻辑
  • 复杂协作场景优先使用阻塞队列简化代码

通过合理运用多线程,能有效提升程序的并发处理能力,构建高效、响应迅速的应用系统。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值