并发关注点分离的重构方法
立即解锁
发布时间: 2025-08-20 00:56:37 阅读量: 1 订阅数: 12 

### 并发关注点分离的重构方法
#### 1. 引言
在多核/众核计算机上,并发编程日益流行,但软件工程实践却远远落后于硬件发展。并发编程与传统编程不同,它不仅要遵循传统软件开发的设计原则,还需具备高性能、无数据竞争和良好的模块化特性。
关注点分离是软件工程实践中最重要的原则之一。像日志记录、身份验证和异常处理等横切关注点会影响整个应用程序,应尽可能集中处理。并发也是一种典型的非功能属性,将并发关注点从软件中分离出来,能让并发软件的结构更清晰,更易于维护。
然而,与分离其他关注点相比,分离并发关注点并非易事。主要挑战包括:
- 如何从并发软件中分离并发关注点并封装成模块,因为JDK中提出了许多并发类,且很多类可实现相同功能。
- 如何在不牺牲性能的前提下重构软件。
- 如何确保在进行并发重构时软件的安全性。
为应对这些挑战,提出了一种基于面向方面编程(AOP)的重构方法,将线程、同步、屏障和线程通信等操作重构为独立的方面,使并发得到统一管理,并可在需要的代码位置复用,且不会出现在最终软件的核心功能中。
#### 2. 动机示例
为说明重构的难度,给出两个Java示例,程序员可能使用两种不同的方式来编写与线程相关的操作。
示例一:
```java
public class Test implements Runnable{
public Test(){
}
public void run(){
synchronized(this){
testMeth();
}
}
private void testMeth(){//…}
public static void main(String[] args){
Test test1 = new Test();
Thread t1 = new Thread(test1);
t1.start();
Test test2 = new Test();
Thread t2 = new Thread(test2);
t2.start();
try {
t1.join(); t2.join();
} catch (InterruptedException e) {
}
//…
}
}
```
示例二:
```java
public class Test extends Thread{
CyclicBarrier barrier;
public Test(CyclicBarrier barrier){
this.barrier = barrier;
}
public void run(){
testMeth();
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) { }
}
private synchronized void testMeth(){//…}
public static void main(String[] args){
CyclicBarrier barrier = new CyclicBarrier(3);
Thread t1 = new Test(barrier);
t1.start();
Thread t2 = new Test(barrier);
t2.start();
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) { }
//…
}
}
```
这两个示例虽然形式不同,但实现了相同的功能,重构操作却完全不同。重构的难点包括:
- 需要识别并发关注点(所有下划线标注的代码)。
- 对这些并发关注点进行重构时,可能需要应用不同的重构操作,特别是同步和屏障等操作,必须谨慎调整。例如,在重构屏障操作时,示例一需要将所有线程注册到一个集合中并调用`join()`方法,而示例二则需要定义新的屏障对象并调用`await()`方法。
- 需要将它们重构为一个独立的模块,以统一管理这些并发关注点。
#### 3. 重构框架
##### 3.1 概述
重构的目标是将并发关注点从并发软件中分离出来,使并发集中化。这样,并发软件将易于维护,并发关注点也能得到统一管理。
重构框架的概述如下:
```mermaid
graph LR
A[并发软件] --> B[识别并发关注点]
B --> C[删除核心功能代码中的并发关注点]
C --> D[重构为方面]
D --> E[应用方面到类]
```
将并发软件作为输入,首先从核心功能中识别出线程、同步、屏障和线程通信等并发关注点,然后从核心功能代码中删除这些并发关注点,并将它们重构为方面(AOP机制)进行封装。为使方面可复用,将每种操作重构为一个单独的方面。在将方面应用于类时,通过重写切入点来复用现有方面,而通知保持不变。但对于同步操作,需要根据每个切入点的情况进行处理。
方面在重构框架中统一管理,处理每种线程操作的方面集中在一个文件中。一些方面可能应用于相同的连接点,方面的顺序由方面的优先级指定。
##### 3.2 识别并发关注点
Java自JDK出现以来提供了许多类和接口来支持并发编程。实现相同功能可能会使用不同的类和接口。并发关注点主要包括:
- 线程操作:包括创建和运行操作,如`start()`、`interrupt()`和`join()`等。
- 同步操作:有同步方法和同步块两种常见形式。同步方法将`synchronized`修饰符放在方法头部,整个方法受同步锁保护;同步块通常以`synchronized`关键字后跟监视器对象开始,可保护方法中的小部分代码。
- 线程通信和屏障操作:如`notify()`、`wait()`和`barrier()`等。
##### 3.3 重构
- 线程操作重构:
- 进行重构时,需要从类的头部移除对`Thread`类的扩展或`Runnable`接口的实现,然后将线程代码移动到一个方面中。创建方面时,将`run()`方法的头部作为切入点,定义
0
0
复制全文
相关推荐










