Skip to content

Commit 5b3f90e

Browse files
LIANGLINJIANG888LIANGLINJIANG888
authored andcommitted
docs: 更新12306专栏文章
1 parent f2eb13e commit 5b3f90e

File tree

4 files changed

+294
-0
lines changed

4 files changed

+294
-0
lines changed

docs/.vuepress/config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,10 @@ module.exports = {
918918
"死磕设计模式之抽象责任链模式.md",
919919
"策略模式在项目设计中的应用.md",
920920
"死磕设计模式之抽象策略模式.md",
921+
"Builder模式在项目设计中的应用.md",
922+
"单例+简单工厂模式在项目设计中的应用.md",
923+
924+
921925

922926
]
923927
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# **使用JUC中的核心组件来优化业务功能性能**
2+
3+
## JUC简介:
4+
在Java并发编程中,`java.util.concurrent`(简称JUC)工具类库提供了强大的线程管理和任务执行的工具,它允许开发者能够更加容易地写出高效且线程安全的代码。使用JUC进行优化可以显著提升系统的性能和响应能力,同时降低开发复杂性。本文将介绍如何使用JUC中的一些核心组件来优化业务功能性能。
5+
6+
### JUC的核心组件
7+
8+
JUC包含了许多用于处理并发的实用工具,其中一些关键的组件包括:
9+
10+
1. **ExecutorService 和 ThreadPoolExecutor**:提供线程池管理,可以有效地重用线程,减少创建和销毁线程的开销。
11+
2. **CountDownLatch 和 CyclicBarrier**:用于协调多个线程之间的同步操作。
12+
3. **Semaphore**:限流器,用于控制同时访问资源的线程数量。
13+
4. **Future 和 CompletableFuture**:代表异步计算的结果,允许应用程序在计算完成之前继续执行其他任务。
14+
5. **ConcurrentHashMap** 和其他并发集合:提供高并发的数据结构,支持高效的并发访问。
15+
16+
### 使用JUC优化业务功能
17+
18+
#### 线程池优化
19+
20+
线程池是管理线程的强大工具,它可以极大地减少在执行大量异步任务时因频繁创建和销毁线程而产生的性能开销。使用`Executors`类可以方便地创建一个线程池:
21+
22+
```java
23+
ExecutorService executor = Executors.newFixedThreadPool(10);
24+
```
25+
26+
在需要执行任务时,只需将`Runnable``Callable`任务提交给线程池:
27+
28+
```java
29+
executor.submit(() -> {
30+
// 业务逻辑代码
31+
});
32+
```
33+
34+
当所有任务完成后,记得关闭线程池以释放资源:
35+
36+
```java
37+
executor.shutdown();
38+
```
39+
40+
#### 同步工具类优化
41+
42+
在多线程环境下对数据进行操作时,可以使用`CountDownLatch``CyclicBarrier``Semaphore`等同步辅助类来控制线程的执行顺序和数量。例如,使用`CountDownLatch`确保所有线程都准备好之后再开始执行:
43+
44+
```java
45+
CountDownLatch latch = new CountDownLatch(N);
46+
for (int i = 0; i < N; i++) {
47+
new Thread(() -> {
48+
// 准备工作...
49+
latch.countDown();
50+
try {
51+
latch.await(); // 等待所有线程准备完毕
52+
} catch (InterruptedException e) {
53+
Thread.currentThread().interrupt();
54+
}
55+
// 执行任务...
56+
}).start();
57+
}
58+
```
59+
60+
#### 异步编程优化
61+
62+
利用`CompletableFuture`可以实现异步编程,避免阻塞主线程,提高系统吞吐量。下面的例子展示了如何使用`CompletableFuture`异步执行任务并在结果可用时进行处理:
63+
64+
```java
65+
CompletableFuture.supplyAsync(() -> {
66+
// 耗时操作...
67+
return result;
68+
}).thenAccept(result -> {
69+
// 处理结果...
70+
});
71+
```
72+
73+
#### 并发集合的使用
74+
75+
使用并发集合如`ConcurrentHashMap`可以在多线程环境下安全地进行数据操作,而无需外部同步:
76+
77+
```java
78+
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
79+
map.put("key", "value");
80+
```
81+
82+
### 结论
83+
84+
通过合理运用JUC提供的工具,我们可以显著提升业务功能的并发处理能力,减少资源消耗,并简化多线程编程的复杂性。无论是线程池管理、线程同步控制,还是异步编程和并发数据结构,JUC为我们提供了一套全面的解决方案。然而,值得注意的是,虽然JUC提供了很多便捷的工具,但正确使用它们要求开发者理解并发编程的原理和细节。错误的使用方法可能会导致难以发现的并发问题。因此,在使用JUC进行系统性能优化时,建议仔细测试和审查代码。
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# **分布式锁在项目设计中的应用**
2+
3+
## 分布式锁简介
4+
分布式锁是一种在分布式系统中用于协调多个进程或服务之间共享资源访问的同步机制。在项目设计中,分布式锁的应用非常关键,尤其是在需要确保多个节点上的操作的原子性、一致性和排他性时。
5+
6+
### 分布式锁在项目设计中的几种典型应用场景:
7+
8+
- 1. 数据库操作的同步
9+
在分布式系统中,多个服务可能需要访问和修改同一份数据库资源。为了防止数据不一致和并发问题,可以使用分布式锁来确保同一时间只有一个服务能够执行特定的数据库操作。
10+
- 2. 缓存更新的同步
11+
分布式系统中的缓存更新操作需要保证原子性,以防止缓存数据不一致。例如,当一个服务更新缓存时,可以使用分布式锁来阻止其他服务同时对相同的缓存键进行操作。
12+
- 3. 任务的串行执行
13+
在处理一些需要串行执行的任务时,分布式锁可以确保任务在分布式环境下按照预定的顺序执行。例如,定时任务、批处理作业等,可以通过分布式锁来控制任务的启动和执行。
14+
- 4. 资源的独占访问
15+
在分布式系统中,某些资源(如文件、网络连接等)可能需要独占访问。分布式锁可以用来确保在任何给定时间,只有一个服务能够访问这些资源。
16+
- 5. 限流和节流
17+
分布式锁可以用于实现分布式环境下的限流和节流策略。例如,通过限制同时访问某个服务的请求数量,可以防止系统过载。
18+
- 6. 领导选举
19+
在分布式系统中,领导选举算法(如Raft或ZooKeeper的Leader Election)通常会用到分布式锁来确保集群中的一个节点成为领导者,并对系统状态进行协调。
20+
21+
### 实现分布式锁的技术
22+
实现分布式锁的技术有多种,包括但不限于:
23+
24+
- 基于数据库:使用数据库事务和唯一索引来实现锁。
25+
- 基于缓存系统:使用Redis的SETNX(SET if Not eXists)命令或Redisson等库来实现锁。
26+
- 基于分布式协调服务:使用ZooKeeper、etcd等分布式协调服务来实现锁。
27+
- 基于消息队列:使用消息队列的顺序消息特性来实现分布式锁。
28+
29+
## 设计考虑
30+
在使用分布式锁时,需要考虑以下几个设计要点:
31+
32+
- 性能:锁的获取和释放需要高效,以免成为系统瓶颈。
33+
- 可靠性:锁的实现必须可靠,确保在分布式环境下的一致性和可用性。
34+
- 死锁处理:需要有机制来处理死锁情况,例如通过设置锁的超时时间。
35+
- 容错性:在分布式锁的实现中,需要考虑节点故障和网络分区的情况。
36+
- 可扩展性:随着系统规模的扩大,分布式锁的实现应当能够适应更多的并发访问。、
37+
38+
## 分布式锁在项目中的实现和应用
39+
// todo
40+
41+
## 总结
42+
43+
综上所述,分布式锁在项目设计中扮演着关键角色,它有助于确保分布式环境下的数据一致性和操作的原子性。在设计和实现分布式锁时,需要权衡多种因素,选择最适合项目需求的技术方案。
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# **单例+简单工厂模式在项目设计中的应用**
2+
3+
## 单例模式简介:
4+
5+
单例模式(Singleton Pattern)是一种常用的软件设计模式,它保证一个类仅有一个实例,并提供该实例的全局访问点。这种模式通常用于管理共享资源、控制对资源的访问以及在多线程环境中避免创建多个对象实例导致的冲突和资源浪费。
6+
7+
### 单例模式的优点与缺点
8+
9+
**优点:**
10+
- 对资源的节约:对于频繁使用的对象,避免了对象的频繁创建和销毁。
11+
- 全局访问点:提供了全局的访问点供外界获取唯一的实例。
12+
- 确保单一实例:保证了系统中只有一个实例对象,有助于协调多个部分的操作。
13+
14+
**缺点:**
15+
- 延迟加载:无法通过引用传递进行依赖注入。
16+
- 反序列化重新创建对象:默认的反序列化机制会破坏单例,需要特殊处理。
17+
- 线程安全:在某些实现中可能需要处理多线程环境下的同步问题。
18+
- 违反单一职责原则:单例类的职责除了维护自己的单一实例外,还要兼顾自己的业务逻辑。
19+
20+
## 简单工厂模式简介:
21+
22+
简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的最佳方式。在这种模式中,一个类负责创建其他类的实例,而不需要使客户端代码与具体的类有所依赖。这样做的好处是,客户端代码与具体的类实现解耦,使得系统更加灵活,易于扩展和维护。
23+
24+
### 简单工厂模式的优点与缺点
25+
26+
**优点:**
27+
28+
- 减少耦合:客户端代码不需要知道具体的产品类,只需要知道如何通过工厂获取产品。
29+
- 易于扩展:当需要添加新的产品时,只需添加具体产品类,并在工厂类中添加相应的逻辑,而无需修改现有的客户端代码。
30+
- 提高代码的可维护性:由于对象的创建和使用是分离的,这使得代码更加清晰,易于维护。
31+
32+
**缺点:**
33+
34+
- 工厂类职责过重:随着产品种类的增加,工厂类的责任会变得越来越重,难以维护。
35+
- 可抽象程度有限:如果产品类别之间差异很大,可能不容易找到一个通用的超级类或接口,这时简单工厂模式可能不适用。
36+
37+
38+
## 单例+简单工厂模式在项目设计中的应用
39+
40+
在12306项目中,通过单例模式实现单例容器,存放共享资源,并提供全局访问点。通过简单工厂模式,实现座位检查方法实例的创建,提高代码的可读性和灵活性。
41+
项目中的单例模式采用饿汉式生成单例容器,代码如下:
42+
#### 单例容器 饿汉式(静态常量)
43+
44+
```java
45+
/**
46+
* 单例对象容器
47+
*/
48+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
49+
public final class Singleton {
50+
51+
private static final ConcurrentHashMap<String, Object> SINGLE_OBJECT_POOL = new ConcurrentHashMap();
52+
53+
/**
54+
* 根据 key 获取单例对象
55+
*/
56+
public static <T> T get(String key) {
57+
Object result = SINGLE_OBJECT_POOL.get(key);
58+
return result == null ? null : (T) result;
59+
}
60+
61+
/**
62+
* 根据 key 获取单例对象
63+
*
64+
* <p> 为空时,通过 supplier 构建单例对象并放入容器
65+
*/
66+
public static <T> T get(String key, Supplier<T> supplier) {
67+
Object result = SINGLE_OBJECT_POOL.get(key);
68+
if (result == null && (result = supplier.get()) != null) {
69+
SINGLE_OBJECT_POOL.put(key, result);
70+
}
71+
return result != null ? (T) result : null;
72+
}
73+
74+
/**
75+
* 对象放入容器
76+
*/
77+
public static void put(Object value) {
78+
put(value.getClass().getName(), value);
79+
}
80+
81+
/**
82+
* 对象放入容器
83+
*/
84+
public static void put(String key, Object value) {
85+
SINGLE_OBJECT_POOL.put(key, value);
86+
}
87+
```
88+
### 简单工厂 + 单例容器 实现座位通过BitMap检测抽象工厂
89+
```java
90+
/**
91+
* 座位通过 BitMap 检测抽象工厂
92+
*
93+
*/
94+
public abstract class BitMapCheckSeatStatusFactory {
95+
96+
public static final String TRAIN_BUSINESS = "TRAIN_BUSINESS";
97+
public static final String TRAIN_FIRST = "TRAIN_FIRST";
98+
public static final String TRAIN_SECOND = "TRAIN_SECOND";
99+
100+
/**
101+
* 获取座位检查方法实例
102+
*
103+
* @param mark 座位标识
104+
* @return 座位检查类
105+
*/
106+
public static BitMapCheckSeat getInstance(String mark) {
107+
BitMapCheckSeat instance = null;
108+
switch (mark) {
109+
case TRAIN_BUSINESS -> {
110+
instance = Singleton.get(TRAIN_BUSINESS);
111+
if (instance == null) {
112+
instance = new TrainBusinessCheckSeat();
113+
Singleton.put(TRAIN_BUSINESS, instance);
114+
}
115+
}
116+
case TRAIN_FIRST -> {
117+
instance = Singleton.get(TRAIN_FIRST);
118+
if (instance == null) {
119+
instance = new TrainFirstCheckSeat();
120+
Singleton.put(TRAIN_FIRST, instance);
121+
}
122+
}
123+
case TRAIN_SECOND -> {
124+
instance = Singleton.get(TRAIN_SECOND);
125+
if (instance == null) {
126+
instance = new TrainSecondCheckSeat();
127+
Singleton.put(TRAIN_SECOND, instance);
128+
}
129+
}
130+
}
131+
return instance;
132+
}
133+
}
134+
135+
```
136+
### 购票场景中使用
137+
```java
138+
private Pair<List<TrainPurchaseTicketRespDTO>, Boolean> findMatchSeats(SelectSeatDTO requestParam, List<String> trainCarriageList, List<Integer> trainStationCarriageRemainingTicket) {
139+
TrainSeatBaseDTO trainSeatBaseDTO = buildTrainSeatBaseDTO(requestParam);
140+
int chooseSeatSize = trainSeatBaseDTO.getChooseSeatList().size();
141+
List<TrainPurchaseTicketRespDTO> actualResult = Lists.newArrayListWithCapacity(trainSeatBaseDTO.getPassengerSeatDetails().size());
142+
BitMapCheckSeat instance = BitMapCheckSeatStatusFactory.getInstance(TRAIN_BUSINESS);
143+
HashMap<String, List<Pair<Integer, Integer>>> carriagesSeatMap = new HashMap<>(4);
144+
int passengersNumber = trainSeatBaseDTO.getPassengerSeatDetails().size();
145+
for (int i = 0; i < trainStationCarriageRemainingTicket.size(); i++) {
146+
String carriagesNumber = trainCarriageList.get(i);
147+
List<String> listAvailableSeat = seatService.listAvailableSeat(trainSeatBaseDTO.getTrainId(), carriagesNumber, requestParam.getSeatType(), trainSeatBaseDTO.getDeparture(), trainSeatBaseDTO.getArrival());
148+
int[][] actualSeats = new int[2][3];
149+
for (int j = 1; j < 3; j++) {
150+
for (int k = 1; k < 4; k++) {
151+
actualSeats[j - 1][k - 1] = listAvailableSeat.contains("0" + j + SeatNumberUtil.convert(0, k)) ? 0 : 1;
152+
}
153+
}
154+
List<Pair<Integer, Integer>> vacantSeatList = CarriageVacantSeatCalculateUtil.buildCarriageVacantSeatList2(actualSeats, 2, 3);
155+
boolean isExists = instance.checkChooseSeat(trainSeatBaseDTO.getChooseSeatList(), actualSeats, SEAT_Y_INT);
156+
// 省略其他代码
157+
}
158+
}
159+
```
160+
161+
### 结论
162+
163+
在实际应用中,应根据具体的应用场景选择合适的实现方式,并注意其可能带来的问题。正确地使用设计模式可以提高代码扩展性、灵活性和复用性,但如果过度使用或不当使用,也可能带来不必要的复杂性和潜在的问题。

0 commit comments

Comments
 (0)