下载工作台
Java 编程实战

多线程与并发基础

试读上半部分 · 解锁后可读全文

第 10 章 · 多线程与并发基础

本章目标:理解 ThreadRunnable 创建线程的方式;使用 ExecutorService 线程池管理任务;掌握 synchronized 同步入门;了解 volatile 与可见性;与 Python 多线程对照,明确 Java 线程适用场景。

学时建议:5~6 小时(含 2 小时跟练)

前置ch05 类与对象;ch08 集合(注意线程安全)。


10.1 进程与线程

进程(Process)— 资源分配单位,独立内存空间
  └── 线程(Thread)— CPU 调度单位,共享进程内存
概念说明
多线程同一进程内并发执行多个任务
并发逻辑上同时(单核切换)
并行物理上同时(多核)

toolkit-demo 场景:同时解析多个日志文件、批量导出 CSV 时,可用线程池并行 IO。


10.2 创建线程:Thread 与 Runnable

方式一:继承 Thread(少用)

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程运行: " + getName());
    }
}

MyThread t = new MyThread();
t.start();  // 启动线程,勿直接调用 run()

方式二:实现 Runnable(推荐)

Runnable task = () -> {
    for (int i = 0; i < 5; i++) {
        System.out.println(Thread.currentThread().getName() + " " + i);
    }
};

Thread t1 = new Thread(task, "worker-1");
t1.start();
要点说明
start()启动新线程执行 run()
直接 run()仅在当前线程执行,无并发
线程名便于日志排查

10.3 线程生命周期

新建 → 就绪 → 运行 → 阻塞/等待 → 终止
         ↑___________|
状态触发
NEW创建未 start
RUNNABLE可运行
BLOCKED等待锁
WAITINGwait() / join()
TERMINATED执行完毕
Thread t = new Thread(() -> System.out.println("done"));
t.start();
t.join();  // 主线程等待 t 结束
System.out.println("主线程继续");

10.4 ExecutorService 线程池

手动 new Thread 难以管理数量与复用;线程池统一调度:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class PoolDemo {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(4);

        for (int i = 0; i < 10; i++) {
            int taskId = i;
            pool.submit(() -> {
                System.out.println("任务 " + taskId + " 由 " +
                    Thread.currentThread().getName() + " 执行");
            });
        }

        pool.shutdown();          // 不再接受新任务
        pool.awaitTermination(30, TimeUnit.SECONDS);
        System.out.println("全部完成");
    }
}
工厂方法特点
newFixedThreadPool(n)固定 n 线程,任务队列
newCachedThreadPool()线程数弹性,适合短任务
newSingleThreadExecutor()单线程顺序执行

生产环境:优先使用 ThreadPoolExecutor 显式配置核心数、队列、拒绝策略,避免 Executors 无界队列 OOM。


10.5 Callable 与 Future

需要返回值时用 Callable

import java.util.concurrent.*;

ExecutorService pool = Executors.newFixedThreadPool(2);

Future<Integer> future = pool.submit(() -> {
    Thread.sleep(500);
    return 42;
});

System.out.println("等待结果...");
int result = future.get();  // 阻塞直到完成
System.out.println(result);

pool.shutdown();
API说明
submit(Callable)返回 Future
future.get()阻塞取结果
future.get(timeout, unit)限时等待
invokeAll(tasks)批量提交

10.6 共享数据与 synchronized

多线程访问同一变量需同步,否则出现竞态:

public class Counter {
    private int count = 0;

  // 不安全版本
    public void incrementUnsafe() {
        count++;
    }

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

    public synchronized int getCount() {
        return count;
    }
}
Counter c = new Counter();
ExecutorService pool = Executors.newFixedThreadPool(10);

for (int i = 0; i < 1000; i++) {
    pool.submit(() -> c.increment());
}
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);
System.out.println(c.getCount());  // 应为 1000

synchronized

  • 修饰方法:锁当前对象(实例方法)或类(静态方法)
  • 同步块:synchronized (obj) { ... },缩小锁粒度
问题synchronized 作用
竞态条件同一时刻仅一线程进入
内存可见性释放锁时刷新主内存

10.7 锁对象与同步块

public class BankAccount {
    private final Object lock = new Object();
    private double balance;

    public void deposit(double amount) {
        synchronized (lock) {
            balance += amount;
        }
    }

    public double getBalance() {
        synchronized (lock) {
            return balance;
        }
    }
}

原则:锁的对象应私有、不可变;避免锁字符串字面量或 this 被外部访问。


以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。