线程与进程的基本概念

进程是操作系统资源分配的基本单位,拥有独立的地址空间、数据栈和其他系统资源。每个进程运行在独立的内存空间中,相互隔离。

线程是进程内的执行单元,共享进程的地址空间和资源。一个进程可以包含多个线程,线程之间可以直接访问同一进程的数据。

资源分配与开销

进程需要独立的系统资源分配,包括内存、文件描述符等。创建和销毁进程的开销较大,上下文切换成本高。

线程共享进程的资源,创建和销毁的开销较小。线程间切换只需保存少量寄存器状态,效率更高。

通信方式

进程间通信(IPC)需要通过特定机制实现,如管道、消息队列、共享内存等。这些机制通常涉及系统调用,速度较慢。

线程间通信可以直接读写共享内存,速度快且简单。但需要同步机制(如互斥锁)来避免竞争条件。

稳定性与安全性

进程崩溃不会直接影响其他进程,系统稳定性较高。进程间的隔离性提供了更好的安全性。

线程崩溃可能导致整个进程崩溃,影响其他线程。线程共享数据可能引发安全问题,如竞态条件或死锁。

适用场景

进程适用于需要高隔离性和安全性的任务,如独立运行的应用程序或服务。

线程适用于需要高并发和共享数据的任务,如高性能计算或实时系统。

线程生命周期

  • NEW: 线程创建但未启动
  • RUNNABLE: 调用start()后进入可运行状态
  • BLOCKED: 等待获取锁时阻塞
  • WAITING: 调用wait()join()进入无限等待
  • TIMED_WAITING: 通过sleep(time)wait(time)限时等待
  • TERMINATED: 线程执行完毕

多线程实现方式

继承Thread类

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running");
    }
}
// 启动线程
new MyThread().start();

实现Runnable接口

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable running");
    }
}
// 通过Thread启动
new Thread(new MyRunnable()).start();

Callable与Future
适用于需要返回结果的场景:

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> "Callable Result");
System.out.println(future.get()); // 阻塞获取结果
executor.shutdown();

线程同步与锁机制

synchronized关键字
同步方法或代码块:

public synchronized void syncMethod() { /* 临界区 */ }
// 或
synchronized(lockObject) { /* 临界区 */ }

ReentrantLock
更灵活的锁控制:

Lock lock = new ReentrantLock();
lock.lock();
try {
    // 临界区
} finally {
    lock.unlock();
}

volatile变量
保证变量可见性,禁止指令重排序:

private volatile boolean flag = false;

乐观锁与悲观锁的概念

乐观锁假设并发操作不会频繁发生冲突,因此在数据读取时不加锁,只在更新时检查数据是否被其他事务修改。若未被修改,则提交更新;否则回滚或重试。典型实现包括版本号机制(Version)或CAS(Compare-And-Swap)。

悲观锁假设并发操作一定会发生冲突,因此在数据读取时直接加锁,确保其他事务无法修改数据,直到锁释放。典型实现包括数据库的行锁、表锁或编程语言中的互斥量(Mutex)。


主要区别

冲突处理方式
乐观锁通过检测冲突后处理(如重试),悲观锁通过预防冲突(提前加锁)。

性能开销
乐观锁在低冲突场景下性能更高(无锁竞争);悲观锁在高冲突场景更稳定,但加锁可能引发阻塞。

适用场景
乐观锁适合读多写少、冲突概率低的场景(如秒杀库存校验);悲观锁适合写操作频繁、冲突概率高的场景(如银行转账)。


技术实现示例

乐观锁(版本号机制)
更新前检查版本号是否匹配:

UPDATE table SET value = new_value, version = version + 1 
WHERE id = 1 AND version = old_version;

悲观锁(数据库行锁)
使用SELECT ... FOR UPDATE锁定数据:

BEGIN;
SELECT * FROM table WHERE id = 1 FOR UPDATE;
UPDATE table SET value = new_value WHERE id = 1;
COMMIT;


选择建议

  • 系统并发量高且冲突少时,优先考虑乐观锁。
  • 数据一致性要求严格或冲突频繁时,选择悲观锁。
  • 乐观锁需配合重试机制,避免频繁失败。

线程池实战

创建线程池

ExecutorService pool = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
    pool.execute(() -> System.out.println(Thread.currentThread().getName()));
}
pool.shutdown();

ThreadPoolExecutor参数详解
核心参数包括:

  • corePoolSize: 核心线程数
  • maximumPoolSize: 最大线程数
  • keepAliveTime: 空闲线程存活时间
  • workQueue: 任务队列(如LinkedBlockingQueue

并发工具类应用

CountDownLatch
等待多个线程完成:

CountDownLatch latch = new CountDownLatch(3);
new Thread(() -> { latch.countDown(); }).start();
latch.await(); // 阻塞直到计数器归零

CyclicBarrier
线程到达屏障点后统一执行:

CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("All threads arrived"));
new Thread(() -> { barrier.await(); }).start();

实战案例:生产者-消费者模型

class Buffer {
    private Queue<Integer> queue = new LinkedList<>();
    private int capacity = 10;

    public synchronized void produce(int item) throws InterruptedException {
        while (queue.size() == capacity) {
            wait();
        }
        queue.add(item);
        notifyAll();
    }

    public synchronized int consume() throws InterruptedException {
        while (queue.isEmpty()) {
            wait();
        }
        int item = queue.poll();
        notifyAll();
        return item;
    }
}

性能优化与注意事项

  • 避免死锁:按固定顺序获取多把锁
  • 减少锁粒度:使用ConcurrentHashMap等并发集合
  • 线程局部变量ThreadLocal实现线程隔离
  • 异常处理:线程内未捕获异常会导致线程终止

通过合理使用线程池、锁策略及并发工具,可以构建高效稳定的多线程应用。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐