Java 并发核心:单例模式、生产者消费者、定时器及线程池实现
Java 并发编程涵盖单例模式、生产者消费者模型、定时器调度及线程池管理。单例模式通过饿汉或懒汉方式确保全局唯一实例;生产者消费者利用阻塞队列解耦任务生成与处理;定时器支持延迟或周期性任务执行;线程池通过复用线程优化资源消耗,包含核心线程数、拒绝策略等关键配置。掌握这些机制有助于提升系统性能与稳定性。

Java 并发编程涵盖单例模式、生产者消费者模型、定时器调度及线程池管理。单例模式通过饿汉或懒汉方式确保全局唯一实例;生产者消费者利用阻塞队列解耦任务生成与处理;定时器支持延迟或周期性任务执行;线程池通过复用线程优化资源消耗,包含核心线程数、拒绝策略等关键配置。掌握这些机制有助于提升系统性能与稳定性。

单例模式 (Singleton Pattern):是一种常用的设计模式,主要用于确保一个类在整个应用程序中只有一个实例,并提供一个全局访问点。
核心作用
常见实现方式
public class Singleton {
// 类加载时进行实例化
private static final Singleton hungry = new Singleton();
// 全局唯一获取实例的接口
public static Singleton getInstance() {
return hungry;
}
// 构造方法私有化
private Singleton() {}
}
特点:类加载时进行实例化,不存在运行时实例化过程,所以不存在线程安全问题。
缺点:即使后面的场景中没有使用到该实例,也会将该实例创建出来,可能会造成不必要的资源浪费。
class Singleton {
// volatile:禁止指令重排序
private static volatile Singleton lazy = null;
// 创建锁对象
private static final Object object = new Object();
// 全局唯一获取实例的接口
public static Singleton getInstance() {
// 外层 if 判断:优化,提高性能
if (lazy == null) {
// 避免多线程时实例化多个对象
synchronized (object) {
if (lazy == null) {
lazy = new Singleton();
}
}
}
return lazy;
}
// 构造方法私有化
private Singleton() {}
}
实现细节:
- 通过 synchronized 加锁解决线程安全问题。
- 外层 if 判断,减少锁的开销。
- volatile 防止指令重排序 (避免半初始化对象)。
特点:只有在真正需要时才创建实例,减少系统启动时的资源占用,资源利用率高。
缺点:若未正确使用同步机制 (如 synchronized 或 volatile),可能导致多线程环境下实例被多次创建,线程安全实现复杂。
Java 对象初始化流程
- 内存分配阶段:当使用 new 关键字创建对象时,JVM 会在堆内存中为该对象分配空间。
- 对象初始化阶段:对象内存分配完成后,开始执行初始化。
- 引用赋值阶段:内存分配完成后,JVM 将分配的内存地址赋值给引用变量。
Java 对象初始化流程 (指令重排序后)
- 内存分配阶段:当使用 new 关键字创建对象时,JVM 会在堆内存中为该对象分配空间。
- 引用赋值阶段:内存分配完成后,JVM 将分配的内存地址赋值给引用变量。
- 对象初始化阶段:对象内存分配完成后,开始执行初始化。
| 特性 | 懒汉模式 | 饿汉模式 |
|---|---|---|
| 实例化时机 | 第一次使用时 | 类加载时 |
| 资源消耗 | 节省资源 | 可能浪费资源 |
| 线程安全 | 需要额外同步机制 | 天然线程安全 |
| 实现复杂度 | 较复杂 | 简单 |
| 适用场景 | 实例化开销大,延迟加载 | 实例化开销小,不需要延迟加载 |
生产者/消费者模式 (Producer/consumer model):用于协调多个线程或进程之间的任务分配与数据处理。生产者负责生成数据或任务,消费者负责处理这些数据或任务,二者通过共享的缓冲区 (队列) 进行解耦,避免直接依赖。
核心作用
class MyBlockingQueue {
private int head = 0;
private int tail = 0;
private int useSize = 0;
private final String[] array;
public MyBlockingQueue(int capacity) {
array = new String[capacity];
}
// 添加
public synchronized void put(String string) throws InterruptedException {
if (isFull()) {
// 队列满了,等待消费者消耗元素
this.wait();
}
array[tail] = string;
tail = (tail + 1) % array.length;
useSize++;
this.notify();
}
// 删除
public String take() throws InterruptedException {
String ret;
synchronized (this) {
if (useSize <= 0) {
// 队列空了,等待生产者添加元素
this.wait();
}
ret = array[head];
head = (head + 1) % array.length;
useSize--;
.notify();
}
ret;
}
{
useSize >= array.length;
}
}
public class ProducerConsumerDemo {
public static void main(String[] args) {
MyBlockingQueue queue = new MyBlockingQueue(1000);
Thread thread1 = new Thread(() -> {
int n = 1;
while (true) {
try {
queue.put(String.valueOf(n));
System.out.println("生产元素 n = " + n);
n++;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
Thread thread2 = new Thread(() -> {
while (true) {
try {
System.out.println("消费元素 n = " + queue.take());
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread1.start();
thread2.start();
}
}
定时器 (Timer):用于在特定时间间隔或指定时间点执行任务的编程模式,广泛应用于定时任务调度、延迟操作、周期性任务等场景。核心思想是将任务的执行逻辑与时间控制解耦,通过统一的定时器管理多个任务。
核心作用/特点
标准库 Timer 构造方法
// 1. 默认构造方法
// 创建一个 Timer 对象,是一个后台线程,并使用线程的默认名字
public Timer() {
this("Timer-" + serialNumber());
}
// 2. 指定线程名字的构造方法
// 创建一个 Timer 对象,是一个后台线程,并使用指定的线程名字
public Timer(String name) {
thread.setName(name);
thread.start();
}
// 3. 指定是否为后台线程的构造方法
// 传入 true,是后台线程;传入 false,是前台线程
public Timer(boolean isDaemon) {
this("Timer-" + serialNumber(), isDaemon);
}
// 4. 指定线程名字和是否为后台线程的构造方法
public Timer(String name, boolean isDaemon) {
thread.setName(name);
thread.setDaemon(isDaemon);
thread.start();
}
标准库 Timer 的 schedule 方法
public static void main(String[] args) {
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("延迟三秒执行");
}
};
// 使用 Date 对象来指定具体的执行时间
// new Date(System.currentTimeMillis()+1000) 表示当前时间等待 1000ms
timer.schedule(timerTask, new Date(System.currentTimeMillis() + 1000));
}
public static void main(String[] args) {
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("延迟三秒执行");
}
};
// 当前时间等待 1000ms 后第一次执行任务
// 此后每间隔 1000ms 就执行一次任务
timer.schedule(timerTask, new Date(System.currentTimeMillis() + 1000), 1000);
}
public static void main(String[] args) {
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("延迟三秒执行");
}
};
// 当前时间延迟 3000ms 后执行
timer.schedule(timerTask, 3000);
}
public static void main(String[] args) {
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("延迟三秒执行");
}
};
// 当前时间延迟 3000ms 后执行
// 此后每间隔 3000ms 就执行一次任务
timer.schedule(timerTask, 3000, 3000);
}
class MyTask implements Comparable<MyTask> {
private final Runnable runnable;
private final long time;
public MyTask(Runnable runnable, long delay) {
this.runnable = runnable;
this.time = System.currentTimeMillis() + delay;
}
public long getTime() {
return this.time;
}
public void run() {
runnable.run();
}
@Override
public int compareTo(MyTask o) {
return (int) (this.time - o.time);
}
}
class MyTime {
private final PriorityQueue<MyTask> queue = new PriorityQueue<>();
public void schedule(Runnable runnable, long delay) {
synchronized (this) {
MyTask myTask = new MyTask(runnable, delay);
queue.offer(myTask);
this.notify();
}
}
{
(() -> {
() {
{
() {
(queue.isEmpty()) {
.wait();
}
queue.peek();
System.currentTimeMillis();
(curTime >= myTask.getTime()) {
myTask.run();
queue.poll();
} {
.wait(myTask.getTime() - curTime);
}
}
} (InterruptedException e) {
(e);
}
}
});
thread.setDaemon();
thread.start();
}
}
线程池:线程池是一种管理和复用线程的编程模式。它预先创建一定数量的线程,在执行任务需要时,将任务分配给这些线程,从而提高运行效率。
核心作用:优化多线程任务的执行效率与管理资源。
特点
标准库线程池构造方法参数
假设现在有一个线程池:核心线程数 2,最大线程数 4,等待队列 2。
- 任务数量<=2(A,B) 时,由核心线程执行任务。
- 2<任务数量<=4(A,B,C,D) 时,核心线程无法同时处理所有任务,未被执行的任务 (C,D) 将会进入等待队列中等待核心线程执行。
- 4<任务数量<=6(A,B,C,D,E,F),此时等待队列也满了,线程池就会开放非核心线程来执行任务,C 和 D 任务继续在等待队列中等待,新添加的 E 和 F 任务由非核心线程来执行任务。
- 任务数量>6,核心线程,等待队列,非核心线程都被任务所占用,仍然无法满足需求,此时就会触发线程池的拒绝策略。
public class MyThreadPoolExecutor {
private final int capacity = 1000; // 阻塞队列
private final MyBlockingQueue queue = new MyBlockingQueue(capacity);
private final List<Thread> list = new ArrayList<>();
// 创建线程
public MyThreadPoolExecutor(int n) {
for (int i = 0; i < n; i++) {
Thread thread = new Thread(() -> {
while (true) {
try {
queue.take().run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread.start();
list.add(thread);
}
}
// 添加任务
public void submit(Runnable runnable) throws InterruptedException {
queue.put(runnable);
}
public {
capacity;
}
List<Thread> {
list;
}
}

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online