跳到内容

元数据卡

  • 前置知识:方法与方法栈(第5章)
  • 预计时间:10 分钟
  • 完成标志:能创建并启动线程,理解竞态条件的概念

为什么需要并发

你的程序一次只能做一件事——读文件的时候不能同时处理用户请求。但现实生活中,我们期望程序"同时"做多件事:下载文件的同时还能继续浏览页面。

并发就是程序同时处理多个任务的能力。在 Java 中,最基本的单位是线程

创建线程两种方式

java
// 方式一:继承 Thread
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程运行中");
    }
}
new MyThread().start();

// 方式二:实现 Runnable(更推荐——不占用继承)
Runnable task = () -> {
    System.out.println("线程运行中");
};
new Thread(task).start();

推荐实现 Runnable,因为 Java 只允许单继承,extends Thread 就没办法继承其他类了。

线程的生命周期

新建 (New) → 就绪 (Runnable) → 运行 (Running) → 死亡 (Terminated)

              阻塞 (Blocked/Waiting)
java
Thread t = new Thread(task);  // 新建
t.start();                     // 就绪→运行
t.join();                      // 等待 t 执行完毕

竞态条件

多个线程同时访问共享数据时可能出问题:

java
class Counter {
    private int count = 0;
    
    public void increment() {
        count++;  // 不是原子操作!
    }
}

两个线程同时执行 count++,本来期望加 2,实际可能只加了 1。因为 count++ 实际上是三步:读 count → 加 1 → 写回 count。两步之间的间隙,另一个线程插了进来。

加锁保护

java
class SafeCounter {
    private int count = 0;
    
    public synchronized void increment() {
        count++;  // synchronized 保证同时只有一个线程能执行
    }
}

synchronized 关键字给方法加锁——同一时刻只有一个线程能进入这个方法。其他线程在外面排队。


常见陷阱

Thread.sleep() 不释放锁!sleep 期间线程仍然持有锁,其他线程进不来。


旅人笔记

线程让程序"同时"做多件事。实现 Runnable 比继承 Thread 更灵活。多个线程读写共享数据需要同步——用 synchronized 加锁保护。竞态条件是并发的头号敌人。

下一步:同步与竞态条件

创建线程只是第一步。多个线程操作共享数据会出什么问题?下一节看 synchronized 怎么管住并发。

看同步详解 →

Built with VitePress | Software Systems Atlas