一、核心概念:进程 vs 线程

1. 进程(Process)

进程是操作系统分配资源的基本单位,是程序的一次运行实例。简单来说:

  • 你双击运行一个 Java 程序(java -jar app.jar),操作系统会为这个程序创建一个独立的 JVM 进程;
  • 每个进程拥有独立的内存空间(代码段、数据段、堆、文件句柄等),进程间相互隔离,通信成本高;
  • 进程是 “重量级” 的,创建 / 销毁 / 切换的系统开销大。

Java 中操作进程:可以通过 Runtime.exec()ProcessBuilder 创建子进程(比如调用系统命令、启动另一个程序),示例:

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class ProcessDemo {
    public static void main(String[] args) throws Exception {
        // 创建进程执行系统命令(Windows 用 dir,Linux/Mac 用 ls)
        ProcessBuilder pb = new ProcessBuilder("ls", "-l");
        Process process = pb.start();

        // 读取进程输出
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line);
        }

        // 等待进程结束并获取退出码
        int exitCode = process.waitFor();
        System.out.println("进程退出码:" + exitCode);
    }
}

2. 线程(Thread)

线程是CPU 调度和执行的基本单位,也叫 “轻量级进程”,隶属于进程:

  • 一个进程可以包含多个线程,所有线程共享进程的内存资源(堆、方法区),但每个线程有自己的栈空间程序计数器(记录当前执行的指令);
  • 线程是 “轻量级” 的,创建 / 销毁 / 切换的系统开销远小于进程;
  • Java 程序的运行本质是 JVM 进程内多个线程的协作(比如主线程、GC 线程、JIT 编译线程)。

3. 进程与线程的核心区别

维度 进程 线程
资源分配 操作系统分配资源的单位 共享所属进程的资源
独立性 独立内存空间,相互隔离 共享内存,耦合度高
系统开销 大(创建 / 销毁 / 切换)
通信方式 管道、套接字、文件等 共享变量、锁、队列等
调度单位 操作系统调度进程 CPU 调度线程

二、Java 中线程的核心知识点

1. 线程的创建方式(4 种)

Java 中创建线程有 4 种常用方式,其中线程池是实际开发的首选:

方式 1:继承 Thread 类(重写 run () 方法)
// 步骤:1. 继承 Thread;2. 重写 run()(线程执行逻辑);3. 调用 start() 启动
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
            try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
        }
    }
}

public class ThreadCreate1 {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.setName("线程1");
        t1.start(); // 启动线程(底层调用 native 方法启动系统线程)
        
        MyThread t2 = new MyThread();
        t2.setName("线程2");
        t2.start();
    }
}
方式 2:实现 Runnable 接口(解耦,推荐)
// 步骤:1. 实现 Runnable;2. 重写 run();3. 传入 Thread 构造器,调用 start()
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
            try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
        }
    }
}

public class ThreadCreate2 {
    public static void main(String[] args) {
        Runnable runnable = new MyRunnable();
        Thread t1 = new Thread(runnable, "线程1");
        Thread t2 = new Thread(runnable, "线程2");
        t1.start();
        t2.start();
    }
}
方式 3:实现 Callable 接口(带返回值、可抛异常)
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

// 步骤:1. 实现 Callable;2. 重写 call()(带返回值);3. 包装为 FutureTask;4. 传入 Thread 启动
class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            sum += i;
        }
        return sum;
    }
}

public class ThreadCreate3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> callable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask, "计算线程");
        t.start();
        
        // 获取返回值(会阻塞直到线程执行完成)
        Integer result = futureTask.get();
        System.out.println("1-100 的和:" + result);
    }
}
方式 4:线程池(Executor 框架,实际开发首选)

避免频繁创建 / 销毁线程的开销,控制并发数:

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

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 创建固定大小的线程池(核心数=5)
        ExecutorService executor = Executors.newFixedThreadPool(5);
        
        // 提交 10 个任务
        for (int i = 0; i < 10; i++) {
            int taskNum = i;
            executor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行任务 " + taskNum);
                try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
            });
        }
        
        // 关闭线程池(先停止接收新任务,再等待已提交任务完成)
        executor.shutdown();
    }
}

2. 线程的生命周期(6 种状态)

Java 中 Thread.State 枚举定义了线程的 6 种状态,状态转换是线程的核心逻辑:

  1. NEW:线程对象已创建,但未调用 start()
  2. RUNNABLE:调用 start() 后,线程处于 “就绪 / 运行” 状态(就绪:等待 CPU 调度;运行:正在执行);
  3. BLOCKED:线程因竞争 synchronized 锁而阻塞;
  4. WAITING:线程无时限等待(如 Object.wait()Thread.join());
  5. TIMED_WAITING:线程有时限等待(如 Thread.sleep(1000)Object.wait(1000));
  6. TERMINATED:线程执行完成或异常终止。

3. 线程安全与同步

多个线程共享资源时会出现 “线程安全问题”(比如多线程卖票导致超卖),Java 提供了同步机制解决:

  • synchronized:关键字,可修饰方法 / 代码块,独占锁,自动释放;
  • Lock 接口(如 ReentrantLock):手动锁,更灵活(可中断、超时获取锁);
  • volatile:保证变量的可见性和禁止指令重排(但不保证原子性)。

示例(synchronized 解决卖票问题):

class Ticket {
    private int count = 10; // 10 张票
    
    // 同步方法:保证同一时间只有一个线程执行
    public synchronized void sell() {
        if (count > 0) {
            System.out.println(Thread.currentThread().getName() + " 卖出第 " + count + " 张票");
            count--;
        }
    }
}

public class TicketDemo {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        // 3 个线程卖票
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                for (int j = 0; j < 5; j++) {
                    ticket.sell();
                    try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
                }
            }, "窗口" + (i+1)).start();
        }
    }
}

三、总结

  1. 核心区别:进程是操作系统分配资源的单位,独立内存;线程是 CPU 调度的单位,共享进程资源,开销更小。
  2. Java 线程:创建方式有继承 Thread、实现 Runnable/Callable、线程池(推荐);有 6 种生命周期状态;通过 synchronized/Lock/volatile 保证线程安全。
  3. 实践建议:实际开发中优先使用线程池(而非手动创建线程),避免资源耗尽;多线程共享资源时必须处理线程安全问题。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐