理论与实践:深入浅出Java线程基础

153 阅读3分钟

理论与实践:深入理解Java并发基础

一、什么是线程?线程基础认知

  • 线程是程序执行的最小单位,可以理解为"轻量级进程"一个进程可以包含多个线程,多个线程共享进程的内存空间和系统资源。例如,银行柜台办理业务时,每个窗口可以看作一个线程,多个窗口同时服务客户,提高效率。

类比场景:超市收银系统

  • 一个收银台(单线程)处理顾客结账时,后续顾客必须排队等待。当开设多个收银通道(多线程),顾客可以并行结账,显著提升效率。但需要解决商品库存同步更新等问题(线程安全)。

Java线程知识体系.png


二、线程的三种创建方式

graph TD
    A[线程创建] --> B[实现Thread类]
    A --> C[实现Runnable接口]
    A --> D[使用Callable+Future]
    
    B -->|缺点| E[单继承限制]
    C -->|优点| F[接口灵活]
    D -->|特点| G[返回结果]
  1. 继承Thread类
class MyThread extends Thread {
    public void run() {
        System.out.println("线程执行");
    }
}
// 启动线程
new MyThread().start();
  1. 实现Runnable接口
class MyRunnable implements Runnable {
    public void run() {
        System.out.println("线程执行");
    }
}
// 启动线程
new Thread(new MyRunnable()).start();
  1. Callable+Future(可获取返回值)
Callable<Integer> task = () -> { return 123; };
FutureTask<Integer> futureTask = new FutureTask<>(task);
new Thread(futureTask).start();
System.out.println(futureTask.get());

场景选择建议

  • 简单任务 → Runnable
  • 需要返回值 → Callable
  • 不推荐直接继承Thread(违反组合优于继承原则)

三、线程生命周期

graph LR
    A[新建状态 NEW] -->|start| B[就绪状态 RUNNABLE]
    B -->|获取CPU时间片| C[运行状态]
    C -->|yield/时间片用完| B
    C -->|wait/sleep/阻塞IO| D[阻塞状态]
    D -->|notify/IO完成| B
    C -->|执行完成| E[终止状态]

常见误区
直接调用run()方法不会启动新线程,只是普通方法调用!


四、线程同步的核心方法

  1. synchronized关键字
// 同步代码块
synchronized(锁对象) {
    // 临界区代码
}
// 同步方法
public synchronized void method() {
    // 方法体
}
  1. Lock接口
Lock lock = new ReentrantLock();
lock.lock();
try {
    // 临界区代码
} finally {
    lock.unlock();
}

五、线程通信的关键方法

// 等待
obj.wait();
// 唤醒单个线程
obj.notify();
// 唤醒所有线程
obj.notifyAll();

六、线程安全的经典案例

// 账户类
class Account {
    private int balance;
    
    public synchronized void deposit(int amount) {
        balance += amount;
    }
    
    public synchronized void withdraw(int amount) {
        if(balance >= amount) {
            balance -= amount;
        }
    }
}

七、避免常见问题

  1. 死锁预防:按固定顺序获取锁
  2. 资源竞争:使用原子类(AtomicInteger)
  3. 线程泄露:正确关闭线程池

八、项目实战场景与代码

场景1:电商库存扣减(线程安全)
public class Inventory {
    private int stock = 100;
    private Lock lock = new ReentrantLock();
    
    public boolean deductStock(int quantity) {
        lock.lock();
        try {
            if(stock >= quantity) {
                stock -= quantity;
                return true;
            }
            return false;
        } finally {
            lock.unlock();
        }
    }
}
// 模拟100人并发抢购
ExecutorService executor = Executors.newFixedThreadPool(20);
IntStream.range(0, 100).forEach(i -> 
    executor.execute(() -> {
        if(inventory.deductStock(1)) {
            System.out.println("抢购成功");
        }
    })
);
场景2:批量文件处理(线程池优化)
public class FileProcessor {
    private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors();
    
    public void processFiles(List<File> files) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            CORE_POOL_SIZE * 2,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),
            new ThreadPoolExecutor.CallerRunsPolicy()
        );
        
        files.forEach(file -> 
            executor.execute(() -> {
                // 解析文件内容
                String content = FileUtils.readFile(file);
                // 写入数据库
                saveToDB(parseContent(content));
            })
        );
        
        executor.shutdown();
    }
}
场景3:异步任务通知(CompletableFuture)
public class OrderService {
    public CompletableFuture<Void> createOrder(Order order) {
        return CompletableFuture.runAsync(() -> {
            // 1. 保存订单
            orderDao.save(order);
        }).thenRunAsync(() -> {
            // 2. 发送短信通知
            smsService.sendPaymentNotice(order);
        }).exceptionally(ex -> {
            // 3. 异常处理
            log.error("订单处理失败", ex);
            return null;
        });
    }
}

九、避坑指南

  1. 线程池参数配置黄金法则
    // 根据任务类型设置队列容量
    int queueSize = (coreSize / taskCostInMs) * 1000;
    
  2. 死锁检测技巧
    使用jstack分析线程堆栈:
    jstack <pid> | grep -i deadlock
    
  3. 上下文切换优化
    • 线程数 = CPU核数 * (1 + 等待时间/计算时间)
    • 使用Disruptor框架替代BlockingQueue

应用场景思维导图

graph TD
    App[多线程应用场景] --> Web[Web服务器]
    App --> Data[大数据处理]
    App --> Async[异步通知]
    App --> Timer[定时任务]
    
    Web --> Request[并发请求处理]
    Web --> Session[用户会话管理]
    
    Data --> ETL[ETL流程]
    Data --> Batch[批量计算]
    
    Async --> SMS[短信通知]
    Async --> Email[邮件发送]
    
    Timer --> Report[日报生成]
    Timer --> Cache[缓存刷新]

性能优化实战

案例:日志记录优化
问题:同步写日志造成IO阻塞
解决方案

public class AsyncLogger {
    private static final BlockingQueue<String> queue = new LinkedBlockingQueue<>(1000);
    private static final ExecutorService writer = Executors.newSingleThreadExecutor();
    
    static {
        writer.execute(() -> {
            while(true) {
                try {
                    String log = queue.take();
                    writeToFile(log);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });
    }
    
    public static void log(String message) {
        queue.offer(message); // 非阻塞写入
    }
}

思维导图总结

graph TD
    Thread[线程核心知识] --> Create[创建方式]
    Thread --> Lifecycle[生命周期]
    Thread --> Sync[同步机制]
    Thread --> Communicate[线程通信]
    Thread --> Pool[线程池]
    
    Create -->|方式1| ExtendThread[继承Thread]
    Create -->|方式2| ImplRunnable[实现Runnable]
    Create -->|方式3| CallableFuture[Callable+Future]
    
    Lifecycle --> New[新建]
    Lifecycle --> Runnable[就绪]
    Lifecycle --> Running[运行]
    Lifecycle --> Blocked[阻塞]
    Lifecycle --> Terminated[终止]
    
    Sync --> Synchronized[同步代码/方法]
    Sync --> Lock[ReentrantLock]
    Sync --> Atomic[原子类]
    
    Communicate --> Wait[wait/notify]
    Communicate --> Condition[Condition对象]
    
    Pool --> Core[核心参数]
    Pool --> Executors[工厂类]
    Pool --> Rejection[拒绝策略]