Java多线程基础详解:从实现到线程安全

发布于:2025-07-21 ⋅ 阅读:(11) ⋅ 点赞:(0)

一、Thread与Runnable实现方式对比

1.1 继承Thread类实现多线程

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running: " + Thread.currentThread().getName());
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.start();
        t2.start();
    }
}

特点

  • 直接继承Thread类并重写run()
  • 每个线程对象独立运行,资源共享需额外处理
  • 受单继承限制,扩展性较弱

1.2 实现Runnable接口方式

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable running: " + Thread.currentThread().getName());
    }
}

public class RunnableDemo {
    public static void main(String[] args) {
        MyRunnable task = new MyRunnable();
        new Thread(task, "Thread-1").start();
        new Thread(task, "Thread-2").start();
    }
}

优势

  • 避免单继承限制,保持类设计灵活性
  • 同一任务对象可被多个线程共享
  • 更符合"组合优于继承"的设计原则

1.3 关键对比

特性 Thread类 Runnable接口
实现方式 继承类 实现接口
资源共享 需独立实例 可共享实例
锁机制 对象锁 需显式同步
线程池适配 需转换为Runnable 直接适配

二、sleep()与wait()深度解析

2.1 核心区别对比

特性 sleep() wait()
方法归属 Thread类(静态方法) Object类(实例方法)
锁行为 不释放锁 释放对象锁
唤醒机制 时间到期自动唤醒 需notify()/notifyAll()
使用场景 定时任务/延迟操作 线程协作/条件等待
异常处理 抛出InterruptedException 同上

2.2 典型使用场景

sleep()示例(定时任务)

// 每秒执行一次任务
while (true) {
    processTask();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}

wait()示例(生产者-消费者)

class SharedQueue {
    private final List<Object> queue = new ArrayList<>();
    
    public synchronized void put(Object obj) {
        while (queue.size() == MAX_SIZE) {
            try {
                wait(); // 队列满时等待
            } catch (InterruptedException e) {}
        }
        queue.add(obj);
        notifyAll();
    }
    
    public synchronized Object take() {
        while (queue.isEmpty()) {
            try {
                wait(); // 队列空时等待
            } catch (InterruptedException e) {}
        }
        Object obj = queue.remove(0);
        notifyAll();
        return obj;
    }
}

2.3 注意事项

  1. wait()必须在同步代码块内调用
  2. 优先使用notifyAll()而非notify()避免死锁
  3. sleep()时间精度受系统调度影响
  4. 避免在循环外使用wait()

三、线程安全经典案例分析

3.1 非原子操作引发的问题

class Counter {
    private int count = 0;
    
    public void increment() {
        count++; // 非原子操作(load→add→store)
    }
    
    public int getCount() {
        return count;
    }
}

public class UnsafeDemo {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                counter.increment();
            }
        });
        
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                counter.increment();
            }
        });
        
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        
        System.out.println("Final count: " + counter.getCount()); // 可能小于20000
    }
}

3.2 解决方案对比

同步方法

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

同步代码块

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

原子类

import java.util.concurrent.atomic.AtomicInteger;

class SafeCounter {
    private AtomicInteger count = new AtomicInteger(0);
    
    public void increment() {
        count.incrementAndGet();
    }
    
    public int getCount() {
        return count.get();
    }
}

3.3 性能优化策略

  1. 缩小同步范围
// 不推荐
public synchronized void process() {
    // 非共享操作...
    count++;
}

// 推荐
public void process() {
    // 非共享操作...
    synchronized (this) {
        count++;
    }
}
  1. 锁分离技术
class AdvancedCounter {
    private final Object readLock = new Object();
    private final Object writeLock = new Object();
    private int count = 0;
    
    public void increment() {
        synchronized (writeLock) {
            count++;
        }
    }
    
    public int getCount() {
        synchronized (readLock) {
            return count;
        }
    }
}
  1. 使用并发容器
// 替代synchronizedList
List<String> list = new CopyOnWriteArrayList<>();

// 替代synchronizedMap
Map<String, Integer> map = new ConcurrentHashMap<>();

四、多线程最佳实践

  1. 优先使用Runnable接口实现多线程
  2. 尽可能缩小同步代码块范围
  3. 优先使用并发工具类替代synchronized
  4. 避免在循环外使用wait()/notify()
  5. 合理使用线程池管理线程生命周期
  6. 对竞争资源使用原子类操作
  7. 始终考虑线程安全问题,即使看似安全的只读操作

通过本文的系统讲解,相信读者已经掌握了Java多线程编程的核心概念和实战技巧。在实际开发中,建议结合JUC包中的高级并发工具(如Lock、Semaphore、CountDownLatch等)来构建更高效稳定的多线程应用。


网站公告

今日签到

点亮在社区的每一天
去签到