当前位置: 首页 > news >正文

Java多线程编程初阶指南

目录

一.线程基础概念

线程是什么?

线程与进程对比

为啥要有线程 

二.线程实现方式 

继承Thread类 

 实现Runnable接口

常规实现方式

 匿名内部类写法

 Lambda表达式写法(Java8+) 

对比总结

 三.Thread 类及常见方法

核心功能

核心构造方法

核心属性获取方法

线程控制核心方法 

启动线程:start()

中断控制:interrupt()

线程等待:join()

线程休眠:sleep()

 四.线程的状态

NEW(新建状态)

RUNNABLE(可运行状态)

BLOCKED(阻塞状态)

 WAITING(无限等待)

TIMED_WAITING(限期等待)

五.线程安全(重点) 

什么是线程安全? 

原子性(Atomicity) 

可见性(Visibility)

有序性(Ordering)

解决之前的线程不安全问题 

synchronized 关键字 

volatile 关键字

原子性问题解决方案 

可见性问题解决方案 

有序性问题解决方案 

六.wait()与notify() 

核心作用解析

1. 线程协调的基石

2. 方法定位

标准使用模板

1. 基础协作模式

 2. 生产者-消费者实现

关键使用原则

1. 必须遵守的黄金法则

2. 选择通知策略

3. 超时控制(避免永久阻塞)

 七.多线程案例

单例模式

阻塞式队列

定时器

线程池

总结-保证线程安全的思路


一.线程基础概念

线程是什么?

线程是操作系统调度的最小执行单元,属于同一进程的多个线程共享进程资源(内存、文件句柄等),每个线程独立执行代码指令序列。

  • ​执行流​​:线程是程序中的独立执行路径,多个线程可并发执行
  • ​轻量级进程​​:相比进程,线程更轻量(创建/销毁更快,共享内存空间)
  • ​主从关系​​:主线程创建子线程,共同完成任务

线程与进程对比

特性进程线程
资源隔离独立内存空间共享进程内存
创建开销高(系统级资源分配)低(仅需栈和程序计数器)
通信效率低(需IPC机制)高(直接内存共享)
上下文切换耗时(内存映射切换)快速(仅寄存器切换)

为啥要有线程 

首先, "并发编程" 成为 "刚需". 单核 CPU 的发展遇到了瓶颈. 要想提高算力, 就需要多核 CPU. 而并发编程能更充分利用多核 CPU 资源. 有些任务场景需要 "等待 IO", 为了让等待 IO 的时间能够去做一些其他的工作, 也需要用到并发编程.

其次, 虽然多进程也能实现 并发编程, 但是线程比进程更轻量. 创建线程比创建进程更快. 销毁线程比销毁进程更快. 调度线程比调度进程更快. 

二.线程实现方式 

继承Thread类 

// 传统继承方式
class CustomThread extends Thread {@Overridepublic void run() {System.out.println("线程ID: " + getId());}
}// 创建启动
new CustomThread().start();

特点​​:

  • 直接继承Thread类
  • 通过重写run()方法定义任务
  • 适用于简单场景,但破坏了Java单继承特性

 实现Runnable接口

常规实现方式
class Task implements Runnable {@Overridepublic void run() {System.out.println("传统Runnable实现");}
}// 创建启动
new Thread(new Task()).start();
 匿名内部类写法
// 匿名内部类实现
new Thread(new Runnable() {@Overridepublic void run() {System.out.println("匿名内部类线程");// 可访问外部final变量}
}).start();

优势​​:

  • 适用于一次性任务
  • 支持访问外部final变量
  • 示例:事件监听等临时性任务
 Lambda表达式写法(Java8+) 
// Lambda简化实现
new Thread(() -> {System.out.println("Lambda表达式线程");// 支持多行代码
}).start();// 单行语句简写
new Thread(() -> System.out.println("极简Lambda线程")).start();

特点​​:

  • 要求接口是函数式接口(单一抽象方法)
  • 自动类型推导
  • 代码简洁度提升
  • 推荐用于简单任务场景
对比总结
实现方式代码行数可复用性灵活性推荐指数
继承Thread5-10★☆☆☆☆
常规Runnable6-8★★★☆☆
匿名内部类3-5★★★★☆
Lambda表达式1-3极高★★★★★

 三.Thread 类及常见方法

核心功能

  • 线程创建与启动
  • 线程状态管理
  • 线程优先级控制
  • 线程中断处理
  • 线程同步协作

核心构造方法

构造方法适用场景
Thread()创建空线程对象
Thread(Runnable target)标准Runnable实现(推荐)
Thread(String name)创建命名线程
Thread(Runnable target, String name)命名+Runnable实现
Thread(ThreadGroup group, ...)线程组管理(高级用法)

核心属性获取方法

方法返回值类型说明
getId()long获取线程唯一ID
getName()String获取线程名称
getState()State获取线程状态
getPriority()int获取优先级(1-10)
isAlive()boolean判断线程是否存活
isInterrupted()boolean中断状态检查(不清除标志)

线程控制核心方法 

启动线程:start()

​作用​​:启动线程执行,使线程进入可运行状态(RUNNABLE)

Thread t = new Thread(task);
t.start();  // 正确启动方式
t.run();    // 错误!在调用线程同步执行

执行流程​​:

  1. JVM创建新线程(从NEW状态转换)
  2. 调用run()方法
  3. 线程进入RUNNABLE状态
  4. 由线程调度器分配CPU时间片

​关键特性​​:

  • 每个线程只能调用一次start()
  • 实际执行顺序由OS调度器决定
  • 直接调用run()方法不会创建新线程

中断控制:interrupt()

​作用​​:设置线程中断标志/唤醒阻塞中的线程

Thread worker = new Thread(() -> {while (!Thread.interrupted()) {try {Thread.sleep(1000);} catch (InterruptedException e) {System.out.println("接收中断信号");break;}}
});
worker.start();
worker.interrupt();  // 发送中断信号

​阻塞状态响应​​:

  • sleep()/wait()/join()会立即抛出InterruptedException

  • 抛出异常后自动清除中断标志

try {Thread.sleep(1000);
} catch (InterruptedException e) {// 此处中断标志已被清除Thread.currentThread().interrupt(); // 恢复中断状态
}

中断标志检查​​:

// 方法1:检查并清除标志(静态方法)
if (Thread.interrupted()) {// 处理中断逻辑
}// 方法2:检查标志不改变状态(实例方法)
if (thread.isInterrupted()) {// 处理中断逻辑
}

线程等待:join()

​作用​​:使当前线程等待目标线程终止

Thread t1 = new Thread(task1);
Thread t2 = new Thread(task2);t1.start();
t1.join(500);  // 最多等待500ms
System.out.println("t1状态: " + t1.getState());t2.start();
t2.join();     // 无限等待直至完成

方法重载​​:

方法签名作用描述
void join()无限等待直至线程终止
void join(long millis)最多等待指定毫秒数
void join(long millis, int nanos)高精度等待(实际精度依赖系统实现)

注意事项​​:

  • 调用join()的线程会进入WAITING/TIMED_WAITING状态
  • 可被interrupt()中断等待
  • 不要在主线程中join自身(导致死锁)

线程休眠:sleep()

​作用​​:使当前线程暂停执行指定时间

try {Thread.sleep(2000);         // 休眠2秒Thread.sleep(1500, 500000); // 1.5秒+500000纳秒
} catch (InterruptedException e) {// 中断处理逻辑
}

核心特性​​:

特性说明
锁保持休眠期间保持持有的所有锁
时间精度实际休眠时间>=参数值(受系统计时器和调度器影响)
中断响应休眠期间收到中断会抛出InterruptedException
状态转换进入TIMED_WAITING状态

​​典型应用场景​​:

  • 模拟耗时操作

  • 控制任务执行频率

  • 防止CPU空转消耗资源

 注意要点​​:

// 错误用法:用sleep实现同步控制
while (!condition) {Thread.sleep(100); // 应使用wait/notify机制
}// 正确用法:定时任务间隔
void run() {while (running) {processTask();Thread.sleep(1000); // 每秒执行一次}
}

 四.线程的状态

状态描述
NEW创建未启动
RUNNABLE可运行(包括就绪和运行中)
BLOCKED等待监视器锁
WAITING无限期等待其他线程通知
TIMED_WAITING带超时的等待
TERMINATED执行完成

NEW(新建状态)

​特征​​:

  • 线程对象已创建
  • 未调用start()方法
  • 未分配系统资源

​触发条件​​:

Thread t = new Thread(() -> {});
System.out.println(t.getState()); // 输出NEW

RUNNABLE(可运行状态)

​子状态​​:

  • Ready:等待CPU时间片
  • Running:正在执行

​触发条件​​:

t.start();  // 从NEW进入RUNNABLE
synchronized(lock) { // 锁释放后从BLOCKED回到RUNNABLE
}

BLOCKED(阻塞状态)

​产生场景​​:

  • 等待进入synchronized代码块
  • 等待进入synchronized方法

​触发条件​​: 

Object lock = new Object();// 线程1
new Thread(() -> {synchronized(lock) {while(true); // 长期持有锁}
}).start();// 线程2
Thread t2 = new Thread(() -> {synchronized(lock) { // 此处阻塞System.out.println("获得锁");}
});
t2.start();
System.out.println(t2.getState()); // 输出BLOCKED

 WAITING(无限等待)

​触发条件​​:

object.wait();   // 未设置超时
thread.join();   // 未设置超时
LockSupport.park();

状态特征​​:

  • 需要其他线程主动唤醒
  • 不占用CPU资源
  • 常见于线程协调场景

TIMED_WAITING(限期等待)

典型方法​​: 

Thread.sleep(1000);
object.wait(500);
thread.join(3000);
LockSupport.parkNanos(1000000);

代码示例​​:

Thread t = new Thread(() -> {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}
});
t.start();
System.out.println(t.getState()); // 输出TIMED_WAITING

​终止条件​​:

  • run()方法正常执行结束
  • 未捕获异常导致线程终止

​触发条件​​:

Thread t = new Thread(() -> {});
t.start();
t.join();
System.out.println(t.getState()); // 输出TERMINATED

五.线程安全(重点) 

什么是线程安全? 

​核心定义​​:当多个线程并发访问共享资源时,程序仍能保持正确行为的状态

​黄金准则​​:当且仅当满足以下条件时,程序才是线程安全的:

  1. 原子性(Atomicity)保证
  2. 可见性(Visibility)保证
  3. 有序性(Ordering)保证

原子性(Atomicity) 

原子性要求对共享资源的操作是不可分割的完整单元,其他线程只能看到操作前或操作后的状态,不能观察到中间状态。 

// 非原子操作示例
public class UnsafeCounter {private int count = 0;public void increment() {count++; // 实际包含三个步骤:读值、修改、写回}
}
//mov eax, [count]  ; 读取当前值到寄存器
//inc eax           ; 寄存器值加1
//mov [count], eax  ; 将新值写回内存

当两个线程交错执行时:

线程A:读取count=5 → 暂停
线程B:完成完整操作count=6
线程A:继续执行,最终count=6(正确值应为7)

可见性(Visibility)

 可见性保证一个线程修改共享变量后,其他线程能立即感知到最新值。

可见性问题根源(Java内存模型)​

  • ​工作内存机制​​:每个线程有独立的工作内存(CPU缓存),优先读取本地缓存而非主内存

  • ​缺乏同步机制​​:普通变量修改后不强制刷新到主内存,其他线程可能读取到过期值

  • ​JIT编译器优化​​:热点代码可能被优化为直接使用寄存器中的值,跳过内存读取

public class VisibilityDemo {// 不加volatile会导致死循环private static boolean flag = true; // 无volatile修饰public static void main(String[] args) throws InterruptedException {new Thread(() -> {while (flag) { // 空循环(无同步操作)}System.out.println("线程退出");}).start();Thread.sleep(1000);flag = false; // 主线程修改标志位}
}

导致死循环的详细原因​

​1 工作内存缓存​

​子线程启动时​​:

  • 从主内存读取flag=true到工作内存

  • 后续循环直接使用工作内存中的副本值

​主线程修改flag后​​:

  • 修改的是主内存中的值(但不确定何时刷新)

  • 子线程的工作内存副本未失效,仍认为flag=true

2 JIT编译器优化​

  • 当循环检测到flag未被修改时,可能进行激进优化:

; 伪汇编代码(优化后)
LOOP:cmp [flag], true  // 被优化删除jmp LOOP          // 直接无限循环
  • ​优化依据​​:循环体内无任何同步操作,假设flag不会改变

3 内存屏障缺失​

普通变量的读写​​不插入内存屏障​​:

  • 主线程修改flag后不强制刷新到主内存

  • 子线程不强制从主内存重新加载值

有序性(Ordering)

 有序性保证程序执行顺序符合代码的先后关系,防止编译器和处理器优化导致的指令重排序。

// 危险的双重检查锁
public class Singleton {private static Singleton instance;public static Singleton getInstance() {if (instance == null) {                 // 第一次检查synchronized (Singleton.class) {if (instance == null) {        // 第二次检查instance = new Singleton(); // 问题根源}}}return instance;}
}

对象初始化过程可能被重排序为:

  1. 分配内存空间
  2. 将引用指向内存空间(此时instance != null)
  3. 初始化对象

导致其他线程可能获得未初始化的实例。

解决之前的线程不安全问题 

synchronized 关键字 

核心作用

  • ​原子性​​:确保代码块内的操作不可分割

  • ​可见性​​:修改后的变量值对其他线程立即可见

  • ​有序性​​:防止代码块内的指令重排序

 使用场景

  • 需要保证复合操作的原子性(如:计数器累加)

  • 多线程共享资源的互斥访问(如:数据库连接池)

  • 需要实现线程间协作(配合wait()/notify()

使用方法 

必须显式指定锁对象​​,推荐使用私有final对象,避免使用this或公共对象

实例锁(对象级别) 

public class OrderService {// 1. 创建私有final锁对象(强制不可修改)private final Object orderLock = new Object();private int stock = 100;public void placeOrder() {// 2. 使用指定锁对象同步synchronized(orderLock) { if(stock > 0) {stock--;System.out.println("下单成功,剩余库存:" + stock);}}}
}

类锁(全局级别) 

public class GlobalCounter {// 1. 创建类级别锁对象(static final)private static final Object CLASS_LOCK = new Object();private static int count = 0;public static void increment() {// 2. 使用类锁对象同步synchronized(CLASS_LOCK) {count++;}}
}

volatile 关键字

核心作用

  • ​可见性​​:变量修改后立即对所有线程可见

  • ​有序性​​:禁止指令重排序优化

使用场景

  • 状态标志位(如:线程终止标志)

  • 单次原子操作(如:long/double类型变量)

  • 双重检查锁定模式(DCL单例)

 使用方法 

 状态标志控制​

public class WorkerThread implements Runnable {private volatile boolean running = true;public void run() {while(running) {// 执行任务...}}public void stop() {running = false;  // 其他线程修改后立即生效}
}

 单例模式实现​

public class Singleton {private static volatile Singleton instance;public static Singleton getInstance() {if(instance == null) {synchronized(Singleton.class) {if(instance == null) {instance = new Singleton();  // volatile防止指令重排序}}}return instance;}
}

 独立观察变量​

public class SensorMonitor {private volatile double temperature;// 多个线程读取温度值public double getCurrentTemp() {return temperature;  // 总是获取最新值}// 专用线程更新温度值public void updateTemp(double newValue) {temperature = newValue;}
}

原子性问题解决方案 

synchronized同步锁 

public class SafeCounter {private int count = 0;public synchronized void increment() {count++; // 保证原子操作的黄金方案}
}

实现原理​​:

  • Monitor锁机制确保同一时刻只有一个线程能访问临界区
  • 内存屏障强制工作内存与主内存同步
  • 可重入设计避免死锁

​适用场景​​:

  • 复杂的复合操作
  • 需要保证操作完整性的关键业务

可见性问题解决方案 

volatile关键字 

public class VisibilitySolution {private volatile boolean flag = true;public void start() {new Thread(() -> {while(flag) { /* 即时可见 */ }}).start();}
}

内存屏障机制​​:

  1. 写操作前插入StoreStore屏障
  2. 写操作后插入StoreLoad屏障
  3. 读操作前插入LoadLoad屏障

​适用限制​​:

  • 仅适用于单个变量的状态标记
  • 不保证复合操作的原子性

有序性问题解决方案 

双重检查锁优化 

public class Singleton {private static volatile Singleton instance;public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton(); }}}return instance;}
}

​volatile的魔法作用​​:

  1. 禁止new操作的指令重排序
  2. 保证对象初始化完成后才赋值引用
  3. 内存屏障阻止其他线程访问未初始化对象

六.wait()与notify() 

核心作用解析

1. 线程协调的基石

wait()notify()是Java线程间通信的基础方法,用于解决以下典型场景:

  • 生产者消费者模式(缓冲队列控制)
  • 任务协调(主从线程协作)
  • 资源调度(线程按条件执行)
2. 方法定位
方法作用描述
wait()释放锁并暂停线程,直到其他线程通知
notify()随机唤醒一个等待线程
notifyAll()唤醒所有等待线程

标准使用模板

1. 基础协作模式
public class TaskCoordinator {private final Object lock = new Object();private boolean isReady = false;// 等待侧代码public void waitForCondition() throws InterruptedException {synchronized(lock) {while(!isReady) {        // 必须使用循环检查lock.wait();         // 释放锁并等待}// 条件满足后执行任务System.out.println("执行核心业务...");}}// 通知侧代码public void notifyCondition() {synchronized(lock) {isReady = true;         // 修改条件状态lock.notifyAll();       // 推荐使用notifyAll}}
}
 2. 生产者-消费者实现
public class MessageBuffer {private final LinkedList<String> queue = new LinkedList<>();private final int maxSize;private final Object lock = new Object();public MessageBuffer(int maxSize) {this.maxSize = maxSize;}// 生产者方法public void produce(String message) throws InterruptedException {synchronized(lock) {while(queue.size() == maxSize) {  // 缓冲区满时等待lock.wait();}queue.add(message);lock.notifyAll();  // 唤醒所有消费者}}// 消费者方法public String consume() throws InterruptedException {synchronized(lock) {while(queue.isEmpty()) {          // 缓冲区空时等待lock.wait();}String message = queue.removeFirst();lock.notifyAll();  // 唤醒所有生产者return message;}}
}

关键使用原则

1. 必须遵守的黄金法则
  • ​同步块内使用​​:必须在synchronized代码块中调用
  • ​循环检查条件​​:防止虚假唤醒(spurious wakeup)
  • ​私有锁对象​​:推荐使用专用Object实例作为锁
// 正确示例:私有锁对象
private final Object lock = new Object();// 错误示例:使用公共对象锁
public Object publicLock = new Object();
2. 选择通知策略
场景策略选择说明
单一等待条件notify()随机唤醒一个线程
多个等待条件notifyAll()避免线程饿死
复杂条件判断notifyAll()确保所有等待线程重新检查条件
3. 超时控制(避免永久阻塞)
public void timedWait() throws InterruptedException {synchronized(lock) {long timeout = 5000; // 5秒超时long remaining = timeout;long startTime = System.currentTimeMillis();while(!condition && remaining > 0) {lock.wait(remaining);remaining = timeout - (System.currentTimeMillis() - startTime);}if(condition) {// 执行后续操作}}
}

 七.多线程案例

单例模式

单例模式是校招中最常考的设计模式之一.

啥是设计模式? 设计模式好比象棋中的 "棋谱". 红方当头炮, 黑方马来跳. 针对红方的一些走法, 黑方应招的时候有 一些固定的套路. 按照套路来走局势就不会吃亏. 软件开发中也有很多常见的 "问题场景". 针对这些问题场景, 大佬们总结出了一些固定的套路. 按照 这个套路来实现代码, 也不会吃亏. 单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例. 这一点在很多场景上都需要. 比如 JDBC 中的 DataSource 实例就只需要一个.

单例模式具体的实现方式, 分成 "饿汉" 和 "懒汉" 两种 

饿汉模式

类加载的同时, 创建实例. 

class Singleton {private static Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}

懒汉模式-单线程版

类加载的时候不创建实例. 第一次使用的时候才创建实例

class Singleton {private static Singleton instance = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

 懒汉模式-多线程版

上面的懒汉模式的实现是线程不安全的.

线程安全问题发生在首次创建实例时. 如果在多个线程中同时调用 getInstance 方法, 就可能导致 创建出多个实例. 一旦实例已经创建好了, 后面再多线程环境调用 getInstance 就不再有线程安全问题了(不再修改 instance 了)

加上 synchronized 可以改善这里的线程安全问题

class Singleton {private static Singleton instance = null;private Singleton() {}public synchronized static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

懒汉模式-多线程版(改进)

以下代码在加锁的基础上, 做出了进一步改动:

  • 使用双重 if 判定, 降低锁竞争的频率.
  • 给 instance 加上了 volatile.
class Singleton {private static volatile Singleton instance = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

阻塞式队列

阻塞队列是什么

阻塞队列是一种特殊的队列. 也遵守 "先进先出" 的原则.

阻塞队列能是一种线程安全的数据结构, 并且具有以下特性:

  • 当队列满的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素.
  • 当队列空的时候, 继续出队列也会阻塞, 直到有其他线程往队列中插入元素.

阻塞队列的一个典型应用场景就是 "生产者消费者模型". 这是一种非常典型的开发模型. 

基础版阻塞队列 

/*** 线程安全的阻塞队列实现* @param <T> 队列元素类型*/
public class SimpleBlockingQueue<T> {// 存储队列元素的数组(循环数组)private final Object[] items;// 当前队列中的元素数量private int count;// 下一个插入位置的索引private int putIndex;// 下一个取出位置的索引private int takeIndex;// 同步锁对象(推荐使用专用锁对象)private final Object lock = new Object();/*** 初始化指定容量的队列* @param capacity 队列容量(必须>0)*/public SimpleBlockingQueue(int capacity) {this.items = new Object[capacity];}/*** 将元素放入队列(队列满时阻塞等待)* @param item 要添加的元素(不能为null)* @throws InterruptedException 等待时被中断抛出*/public void put(T item) throws InterruptedException {synchronized (lock) {// 循环检查队列是否已满(防止虚假唤醒)while (count == items.length) {// 释放锁并进入等待状态lock.wait();}// 插入元素到当前位置items[putIndex] = item;// 更新插入位置(循环处理)if (++putIndex == items.length) putIndex = 0;// 元素计数增加count++;// 唤醒所有等待线程(可能有消费者在等待)lock.notifyAll();}}/*** 从队列取出元素(队列空时阻塞等待)* @return 取出的元素* @throws InterruptedException 等待时被中断抛出*/public T take() throws InterruptedException {synchronized (lock) {// 循环检查队列是否为空(防止虚假唤醒)while (count == 0) {// 释放锁并进入等待状态lock.wait();}// 获取当前取出位置的元素T item = (T) items[takeIndex];// 更新取出位置(循环处理)if (++takeIndex == items.length) takeIndex = 0;// 元素计数减少count--;// 唤醒所有等待线程(可能有生产者在等待)lock.notifyAll();return item;}}
}

定时器

定时器是什么

定时器也是软件开发中的一个重要组件. 类似于一个 "闹钟". 达到一个设定的时间之后, 就执行某个指定 好的代码. 

时间轮定时器 

public class SimpleTimer {private final PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>(Comparator.comparingLong(t -> t.execTime));private final Thread worker;public SimpleTimer() {worker = new Thread(() -> {while (true) {try {Task task = queue.take();long curr = System.currentTimeMillis();if (task.execTime > curr) {Thread.sleep(task.execTime - curr);}task.runnable.run();} catch (InterruptedException e) {break;}}});worker.start();}public void schedule(Runnable task, long delayMs) {queue.put(new Task(task, System.currentTimeMillis() + delayMs));}private static class Task {final Runnable runnable;final long execTime;Task(Runnable runnable, long execTime) {this.runnable = runnable;this.execTime = execTime;}}
}

 使用方法​​:

SimpleTimer timer = new SimpleTimer();
timer.schedule(() -> System.out.println("5秒后执行"), 5000);

线程池

线程池是什么

虽然创建线程 / 销毁线程 的开销

想象这么一个场景:在学校附近新开了一家快递店,老板很精明,想到一个与众不同的办法来经营——店里不雇佣固定员工,而是每次有业务时,临时找一名同学来送快递,送完立即解雇。这就像我们处理任务时“来一个任务,起一个线程”的模式。

然而,老板很快发现问题:每次“招聘+解雇”同学的成本极高。善于变通的老板恍然大悟,明白了为何其他公司都选择长期雇人。于是他制定新规则:将公司业务人员逐步扩张到3人,并根据业务需求灵活调整。

具体运作流程如下:当新业务到来时,老板先检查现有员工数量。若未满3人,则立即雇佣一人送快递;若已达3人,则将业务记录在任务本上,等待现有快递员空闲时处理。这正是线程池的核心模式。

线程池最大的好处就是减少每次启动、销毁线程的损耗

核心线程池实现 

public class SimpleThreadPool {private final BlockingQueue<Runnable> taskQueue;private final List<Worker> workers = new ArrayList<>();public SimpleThreadPool(int coreSize) {taskQueue = new LinkedBlockingQueue<>();for (int i = 0; i < coreSize; i++) {Worker worker = new Worker();workers.add(worker);worker.start();}}public void execute(Runnable task) {taskQueue.offer(task);}private class Worker extends Thread {public void run() {while (!isInterrupted()) {try {Runnable task = taskQueue.take();task.run();} catch (InterruptedException e) {break;}}}}
}

 使用方法​​:

SimpleThreadPool pool = new SimpleThreadPool(4);
pool.execute(() -> System.out.println("执行任务"));

总结-保证线程安全的思路

使用没有共享资源的模型

适用共享资源只读,不写的模型

  • 不需要写共享资源的模型
  •  使用不可变对象

 直面线程安全(重点)

  • 保证原子性
  • 保证顺序性
  • 保证可见性

相关文章:

  • 【实战】oninput 文本框输入实时查询防抖机制实现
  • 数字IC低功耗设计——基础概念和低功耗设计方法
  • 出现了锁等待或死锁现象怎么办?乐观锁?分布式锁了解一下?
  • 前端笔记-Vue3(中)
  • 输入框仅支持英文、特殊符号、全角自动转半角 vue3
  • Sqlserver安全篇之_Sqlcmd命令使用windows域账号认证sqlserver遇到问题如何处理的案例
  • JVM考古现场(二十四):逆熵者·时间晶体的永恒之战
  • 乐视系列玩机---乐视1 x600系列线刷救砖以及刷写第三方twrp 卡刷第三方固件步骤解析
  • 【AI News | 20250422】每日AI进展
  • Java 静态内部类面试题与高质量答案合集
  • 华为仓颉编程语言基础概述
  • 【漫话机器学习系列】215.处理高度不平衡数据策略(Strategies For Highly Imbalanced Classes)
  • 性能比拼: Redis vs Dragonfly
  • 服装印花/印烫环节计算机视觉应用设计方案
  • STL C++详解——priority_queue的使用和模拟实现 堆的使用
  • jenkins pipeline ssh协议报错处理
  • 【MCP Node.js SDK 全栈进阶指南】初级篇(4):MCP工具开发基础
  • 【MCP Node.js SDK 全栈进阶指南】初级篇(3):MCP资源开发基础
  • JavaScript ?? 运算符详解
  • 宏碁笔记本电脑怎样开启/关闭触摸板
  • 外卖江湖战火重燃,骑手、商家、消费者在“摇摆”什么?
  • 特朗普称无意解雇鲍威尔,美国股债汇反弹、黄金高位下跌
  • 当代读书人的暗号:不是拆快递,是拆出版社样书!|世界读书日特辑
  • 规模再创新高,超百款新车首发!上海车展明日开幕
  • 金价新高不断,上金所再发风险提示,黄金能否持续闪耀?
  • 朱雨玲:从前世界第一到兼职运动员,30岁后开始“玩”乒乓