车子的个人博客

正确的事情通常都是优雅的

CountDownLatch详解

概述

CountDownLatch是Java并发包(java.util.concurrent)中的一种同步工具类,允许一个或多个线程等待其他线程完成操作。

核心原理

  • 维护一个计数器,初始化为正数
  • 每当一个任务完成,调用countDown()减1
  • 调用await()的线程阻塞,直到计数器为0

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
int count = 3;
CountDownLatch latch = new CountDownLatch(count);

for (int i = 0; i < count; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 完成");
} finally {
latch.countDown();
}
}).start();
}

latch.await(); // 等待所有线程完成
System.out.println("所有任务完成");
}
}

常见场景

1. 等待多个任务完成

1
2
3
4
5
6
CountDownLatch latch = new CountDownLatch(n);
executors.submit(() -> {
// 执行任务
latch.countDown();
});
latch.await();

2. 启动多个线程后同时执行

1
2
3
4
5
6
7
8
CountDownLatch startLatch = new CountDownLatch(1);
for (int i = 0; i < n; i++) {
new Thread(() -> {
startLatch.await(); // 等待开始信号
// 同时执行
}).start();
}
startLatch.countDown(); // 一次性释放所有线程

注意事项

  • 计数器不能被重置
  • await()可设置超时
  • 不保证线程安全

总结

CountDownLatch是简单实用的线程同步工具,适用于等待多任务完成的场景。

Intellij IDEA Window快捷键整理

窗口操作

  • Alt+Number:切换到第N个标签
  • Ctrl+Tab:切换标签
  • Shift+Escape:隐藏窗口

常用快捷键

  • Ctrl+Shift+I:快速查看实现
  • Ctrl+F12:查看文件结构
  • Ctrl+Shift+E:最近变更文件
  • Ctrl+E:最近文件

总结

掌握快捷键提升效率。

Java并发相关知识点总结

线程基础

创建线程的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 1. 继承Thread
class MyThread extends Thread {
@Override
public void run() {}
}

// 2. 实现Runnable
class MyRunnable implements Runnable {
@Override
public void run() {}
}

// 3. 实现Callable
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "result";
}
}

线程状态

  • NEW:新建
  • RUNNABLE:运行
  • BLOCKED:阻塞
  • WAITING:等待
  • TIMED_WAITING:超时等待
  • TERMINATED:终止

synchronized关键字

1
2
3
4
5
6
7
8
9
10
// 1. 同步方法
public synchronized void method() {}

// 2. 同步代码块
public void method() {
synchronized(this) {}
}

// 3. 同步静态方法
public static synchronized void staticMethod() {}

volatile关键字

  • 保证可见性
  • 不保证原子性
  • 禁止指令重排序
1
volatile boolean flag = true;

ThreadLocal

1
2
3
4
ThreadLocal<String> tl = new ThreadLocal<>();
tl.set("value");
String value = tl.get();
tl.remove();

线程池

1
2
3
4
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.execute(() -> {});
executor.submit(() -> {});
executor.shutdown();

总结

并发编程需要理解线程、锁、volatile、线程池等核心概念,合理使用才能写出高效安全的多线程代码。

Elasticsearch与Spring集成

引入依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

配置

1
2
3
4
spring:
elasticsearch:
rest:
uris: localhost:9200

使用

1
2
3
4
5
6
7
8
9
10
11
12
@Document(indexName = "product")
public class Product {
@Id
private String id;

@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String name;
}

public interface ProductRepository extends ElasticsearchRepository<Product, String> {
List<Product> findByNameContaining(String name);
}

总结

Spring Data Elasticsearch简化了ES集成。

Intellij IDEA @Nullable注解自动添加

设置方法

  1. Settings → Editor → General → Code Completion
  2. 开启 “Add @Nullable annotations”
  3. 选择插入位置:参数/返回值

效果

自动为可能为null的方法参数添加@Nullable注解。

总结

减少空指针异常。

数据仓库概述

什么是数据仓库

数据仓库是面向主题的、集成的、不可更新的、随时间变化的数据集合,用于支持管理决策。

特征

  • 面向主题
  • 集成性
  • 不可更新
  • 随时间变化

ETL流程

  • Extract:抽取
  • Transform:转换
  • Load:加载

总结

数据仓库是企业数据分析的基础。

AtomicReference原子引用详解

概述

AtomicReference提供对引用对象的原子操作,用于解决多线程下对象引用的线程安全问题。

基本用法

1
2
3
4
5
6
7
8
9
10
AtomicReference<User> atomicRef = new AtomicReference<>();

// 设置值
atomicRef.set(new User(1, "Tom"));

// 获取值
User user = atomicRef.get();

// 原子更新
atomicRef.compareAndSet(oldUser, newUser);

CAS操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 成功则返回true
boolean success = atomicRef.compareAndSet(
currentUser,
newUser
);

// 循环重试
while (true) {
User current = atomicRef.get();
User next = update(current);
if (atomicRef.compareAndSet(current, next)) {
break;
}
}

场景

  • 对象属性原子更新
  • 乐观锁实现
  • 无锁数据结构

总结

AtomicReference是实现无锁并发的重要工具,比synchronized更高效。

Future和FutureTask的区别

Future接口

Future是Java异步计算结果的抽象,表示异步任务的执行结果。

1
2
3
4
5
6
7
public interface Future<V> {
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
}

FutureTask

FutureTask是Future的实现类,同时实现了Runnable接口。

1
2
public class FutureTask<V> implements RunnableFuture<V> {}
public interface RunnableFuture<V> extends Runnable, Future<V> {}

使用区别

1
2
3
4
5
6
7
8
9
// Future用法
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> "result");
String result = future.get();

// FutureTask用法
FutureTask<String> task = new FutureTask<>(() -> "result");
new Thread(task).start();
String result = task.get();

适用场景

  • Future:提交给线程池,返回Future
  • FutureTask:可作为Runnable直接在线程中执行

总结

FutureTask更灵活,可单独运行;Future配合线程池使用更方便。

MySQL零散知识点总结

1. COUNT(*) vs COUNT(1) vs COUNT(列)

  • COUNT(*):统计所有行,包括NULL
  • COUNT(1):统计结果与COUNT(*)等价,1可以是任意值
  • COUNT(列):只统计非NULL值

性能:InnoDB中COUNT(*)和COUNT(1)性能相当,因为有专门的优化。

2. WHERE vs HAVING

  • WHERE:过滤行,在分组前生效
  • HAVING:过滤分组后的结果,在GROUP BY后生效
1
2
3
4
5
6
-- 正确用法
SELECT dept, COUNT(*)
FROM employee
WHERE dept != 'IT'
GROUP BY dept
HAVING COUNT(*) > 5;

3. DISTINCT 的注意事项

  • DISTINCT只能放在第一个字段前
  • 多字段去重是基于全部字段的组合去重
  • DISTINCT会触发排序操作,大数据量时性能差

4. UNION vs UNION ALL

  • UNION:去重 + 排序,性能较低
  • UNION ALL:不去重,性能高

5. 分页查询优化

延迟关联

1
2
3
4
5
6
7
8
-- 低效
SELECT * FROM orders ORDER BY id LIMIT 100000, 10;

-- 高效(延迟关联)
SELECT o.*
FROM orders o
INNER JOIN (SELECT id FROM orders ORDER BY id LIMIT 100000, 10) t
ON o.id = t.id;

游标分页

记录上一页最后一条的ID,用ID范围分页

6. 字段类型选择

  • 整型优先于字符串
  • 避免NULL,使用NOT NULL + 默认值
  • 避免TEXT/BLOB作为主键
  • 枚举类型用ENUM

7. 字符串存储

  • 定长用CHAR,变长用VARCHAR
  • VARCHAR(100)和VARCHAR(1000)存储空间相同,但内存占用不同
  • 手机号、身份证用字符串而非数字

8. 时间类型选择

  • 日期用DATE
  • 日期时间用DATETIME或TIMESTAMP
  • 需要时区用TIMESTAMP
  • 需要高精度用DATETIME(6)

9. COUNT(1) 性能优化

  • 近似值用EXPLAIN SELECT COUNT(*) FROM table
  • 加条件用COUNT(*) WHERE status = 1
  • 预先建立计数表

10. INSERT 批量插入

1
2
3
4
5
-- 低效
INSERT INTO t VALUES (1,'a'), (2,'b'), (3,'c');

-- 高效(控制在1000条以内)
INSERT INTO t VALUES (1,'a'), (2,'b'), (3,'c');

11. 常见SQL技巧

  • IFNULL处理NULL值
  • IF(CONDITION, TRUE_VAL, FALSE_VAL) 条件表达式
  • COALESCE 返回第一个非NULL值

12. 容易忽略的问题

  • 字符串比较要加引号
  • 避免在索引列上使用函数
  • 使用EXPLAIN分析SQL
  • 注意SQL注入风险

总结

本文汇总了MySQL开发中常见的零散知识点,虽然都是细节,但在实际开发中能避免很多问题。

JDBC ResultSet获取列名

方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ResultSet rs = stmt.executeQuery("SELECT * FROM user");
ResultSetMetaData metaData = rs.getMetaData();

// 获取列数
int count = metaData.getColumnCount();

// 获取列名
for (int i = 1; i <= count; i++) {
String columnName = metaData.getColumnLabel(i); // 别名
String columnName2 = metaData.getColumnName(i); // 原名
}

// 获取列类型
String typeName = metaData.getColumnTypeName(i);

常用方法

方法 说明
getColumnLabel 获取列别名
getColumnName 获取列名
getColumnType 获取Java类型
getColumnTypeName 获取SQL类型

总结

ResultSetMetaData提供了丰富的元数据信息。

0%