0%

多线程问题集锦

一、如何保证 T1→T2→T3 的执行顺序?

要确保 T2 在 T1 完成后执行,T3 在 T2 完成后执行,最直观的方案是使用Thread.join()方法。join()的核心作用是让当前线程阻塞,等待目标线程执行完毕后再继续运行,从而强制线程按顺序执行。

实现示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class ThreadOrder {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("T1 执行中...");
try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("T1 执行完毕");
}, "T1");

Thread t2 = new Thread(() -> {
try {
t1.join(); // 等待T1执行完毕
} catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("T2 执行中...");
try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("T2 执行完毕");
}, "T2");

Thread t3 = new Thread(() -> {
try {
t2.join(); // 等待T2执行完毕
} catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("T3 执行中...");
try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("T3 执行完毕");
}, "T3");

// 启动顺序不影响最终执行顺序
t3.start();
t2.start();
t1.start();
}
}

执行结果:

阅读全文 »

Java 注解(Annotation)详解:从元注解到注解处理器

注解(Annotation)是 Java 5 引入的一种元数据(metadata)机制,用于为代码添加额外信息(如配置、约束、说明等),不直接影响代码逻辑,但可通过工具(编译器、框架)解析并产生实际作用(如编译检查、代码生成)。本文将从基础概念、元注解、注解处理器等方面全面解析 Java 注解。

注解的基本概念

什么是注解?

注解是一种特殊的 “标记”,可附加在类、方法、字段等程序元素上,格式为 @注解名(属性=值)。例如:

1
2
3
4
5
6
7
// 方法上的@Override注解(标记方法重写)
@Override
public void run() { ... }

// 类上的@Deprecated注解(标记类已过时)
@Deprecated
public class OldClass { ... }

注解的作用

  • 编译检查:如 @Override 确保方法正确重写父类方法,编译器会校验正确性。
  • 代码生成:如 Lombok 的 @Data 自动生成 getter/setter 方法,减少模板代码。
  • 运行时处理:如 Spring 的 @Autowired 实现依赖注入,框架在运行时通过反射解析注解。
  • 文档说明:如 @Deprecated 标记过时元素,javadoc 会包含该信息。

元注解:注解的注解

元注解(Meta Annotation)是用于修饰 “注解” 的注解,定义了注解的适用范围、生命周期、继承性等基本特性。Java 内置了 5 种元注解:

@Target:指定注解的适用位置

用于限制注解可附加的程序元素(如类、方法、字段等),属性为 ElementType 数组,常见取值:

ElementType 取值 含义 示例
TYPE 类、接口、枚举 @Service 用于类
METHOD 方法 @Override 用于方法
FIELD 字段(成员变量) @Autowired 用于字段
PARAMETER 方法参数 @RequestParam 用于参数
CONSTRUCTOR 构造器 自定义注解用于构造器
LOCAL_VARIABLE 局部变量 较少使用
ANNOTATION_TYPE 注解类型本身 元注解修饰其他注解
PACKAGE 用于包信息配置
TYPE_PARAMETER(Java 8+) 类型参数(如泛型 <T> @NonNull <T> T get()
TYPE_USE(Java 8+) 任何类型使用处(如变量声明) List<@NonNull String>
阅读全文 »

非线性结构:树与图的核心概念与特性

非线性结构是指数据元素之间存在一对多或多对多关系的结构,无法用线性序列完整描述。常见的非线性结构包括(一对多)和(多对多),它们广泛应用于层次关系建模(如文件系统)、网络关系描述(如社交网络)等场景。

树(Tree):一对多的层次结构

树是一种具有层次关系的非线性结构,核心特征是存在一个根节点,其余节点分属不同子树,形成一对多的分支关系

树的定义与核心特征

树是由 n(n ≥ 0) 个节点组成的有限集合:

  • n = 0 时,称为空树
  • n > 0 时,有且仅有一个根节点(无前驱节点);其余节点可分为若干个互不相交的子集,每个子集本身也是一棵树(称为子树)。

核心特征

  • 根节点唯一,无前驱。
  • 除根节点外,每个节点有且仅有一个前驱(父节点)。
  • 每个节点可以有任意多个后继(子节点)。

树的关键术语

阅读全文 »

Java 时间操作全解析:从传统类到 Java 8 新 API

在 Java 中,时间操作是日常开发的常见需求,包括获取当前时间、格式化时间、计算时间差等。随着 Java 版本的演进,时间 API 也从早期的 DateCalendar 发展到 Java 8 引入的 java.time 系列(如 LocalDateTimeZonedDateTime),后者解决了传统 API 的线程不安全、设计混乱等问题。本文将全面介绍 Java 中操作时间的主要方式及最佳实践。

传统时间类(Java 8 之前)

1. java.util.Date

Date 是最早期的时间类,存储自 1970 年 1 月 1 日 00:00:00(UTC)以来的毫秒数,但大部分方法已被废弃(如 getYear()getMonth()),仅保留少数核心方法(如 getTime())。

示例:创建 Date 对象
1
2
3
4
5
6
7
8
9
import java.util.Date;

public class DateDemo {
public static void main(String[] args) {
Date now = new Date(); // 获取当前时间
System.out.println(now); // 输出:Thu Aug 14 15:30:45 CST 2025
System.out.println(now.getTime()); // 输出:1755166245000(毫秒时间戳)
}
}
阅读全文 »

数据结构之线性结构:从基础概念到实现细节

线性结构是数据结构中最基础也最常用的一类,其核心特征是数据元素之间存在一对一的线性关系。常见的线性结构包括线性表(顺序表、链表)、队列、栈等。它们在逻辑上形成一条 “直线”,每个元素(除首尾外)有唯一的前驱和后继。

线性结构的核心特征

线性结构的元素间关系可概括为:

  • 存在唯一的 “首元素”(第一个元素)和 “尾元素”(最后一个元素)。
  • 除首元素外,每个元素有且仅有一个 “前驱”(前一个元素)。
  • 除尾元素外,每个元素有且仅有一个 “后继”(后一个元素)。

这种结构使得数据的遍历可以按线性顺序依次进行(如从首到尾)。

线性表:线性结构的基础

线性表是由零个或多个数据元素组成的有限序列,是线性结构的抽象模型。根据存储方式的不同,线性表可分为顺序表(顺序存储)和链表(链式存储)。

1. 顺序表(Sequential List)

顺序表是用连续的存储空间存储元素的线性表,逻辑上的顺序与物理存储顺序一致(类似数组)。

实现原理
  • 底层依赖数组,元素在内存中连续排列,通过下标直接访问。
  • 容量固定(可动态扩容),元素个数(size)≤ 容量(capacity)。
核心操作及时间复杂度
操作 实现逻辑 时间复杂度
访问(get) 通过下标直接定位元素 O(1)
插入(add) 若插入中间位置,需移动后续元素腾出空间 O(n)
删除(remove) 若删除中间元素,需移动后续元素填补空位 O(n)
扩容(ensureCapacity) 复制原数组到更大的新数组 O (n)(触发时)
阅读全文 »