Java 中的脏标记模式:通过变更跟踪优化性能
也称为
- 变更跟踪
- 已修改标志
脏标记设计模式的意图
脏标记设计模式用于通过维护一个布尔标志来避免不必要的计算或资源密集型操作,该标志跟踪对象的 state 是否已更改(“脏”)或保持不变(“干净”)。当此标志被设置时,表示需要再次执行特定操作,例如重新计算或刷新数据,以反映更新后的 state。
脏标记模式的详细说明以及现实世界中的例子
现实世界的例子
想象一个图书馆有一个电子目录系统,用于跟踪哪些书籍被借出和归还。每条书籍记录都有一个“脏标记”,每当书籍被借出或归还时,该标记就会被标记。在每天结束时,图书馆工作人员只查看标记为“脏”的记录以更新实际库存,而不是检查图书馆中的每本书。该系统通过只关注状态已更改的项目来显着减少每天库存检查所需的努力和时间,类似于脏标记设计模式通过仅在必要时执行资源密集型操作来最大程度地减少资源密集型操作的方式。
通俗地说
脏标记设计模式通过使用标志来跟踪对象的 state 何时更改以及何时需要更新来最大程度地减少不必要的操作。
维基百科说
脏位或修改位是与计算机内存块相关联的位,用于指示相应的内存块是否已被修改。当处理器写入(修改)此内存时,脏位被设置。该位指示其关联的内存块已被修改,但尚未保存到存储器中。当内存块要被替换时,会检查其相应的脏位,以查看该块是否需要在被替换之前写回辅助存储器,或者它是否可以简单地被删除。脏位由 CPU 缓存和操作系统中的页面替换算法使用。
Java 中脏标记模式的编程示例
DataFetcher
类负责从文件获取数据。它有一个脏标记,用于指示文件中的数据自上次获取以来是否已更改。
public class DataFetcher {
private long lastFetched;
private boolean isDirty = true;
// Other properties and methods...
}
DataFetcher
类具有一个 fetch 方法,该方法在获取数据之前检查脏标记。如果标志为 true,则它从文件获取数据并将标志设置为 false。如果标志为 false,则它返回先前获取的数据。
public List<String> fetch() {
if (!isDirty) {
return data;
}
data = fetchFromDatabase();
isDirty = false;
return data;
}
World
类使用 DataFetcher
来获取数据。它有一个 fetch
方法,该方法调用 DataFetcher
的 fetch
方法。
public class World {
private final DataFetcher fetcher = new DataFetcher();
public List<String> fetch() {
return fetcher.fetch();
}
}
App
类包含 main
方法,该方法演示了脏标记模式的使用。它创建一个 World
对象并循环从它获取数据。World
对象使用 DataFetcher
来获取数据,而 DataFetcher
仅在脏标记为 true 时从文件获取数据。
@Slf4j
public class App {
public void run() {
final var executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(new Runnable() {
final World world = new World();
@Override
public void run() {
var countries = world.fetch();
LOGGER.info("Our world currently has the following countries:-");
countries.stream().map(country -> "\t" + country).forEach(LOGGER::info);
}
}, 0, 15, TimeUnit.SECONDS); // Run at every 15 seconds.
}
public static void main(String[] args) {
var app = new App();
app.run();
}
}
程序输出如下
12:06:02.612 [pool-1-thread-1] INFO com.iluwatar.dirtyflag.DataFetcher -- world.txt is dirty! Re-fetching file content...
12:06:02.615 [pool-1-thread-1] INFO com.iluwatar.dirtyflag.App -- Our world currently has the following countries:-
12:06:02.616 [pool-1-thread-1] INFO com.iluwatar.dirtyflag.App -- UNITED_KINGDOM
12:06:02.616 [pool-1-thread-1] INFO com.iluwatar.dirtyflag.App -- MALAYSIA
12:06:02.616 [pool-1-thread-1] INFO com.iluwatar.dirtyflag.App -- UNITED_STATES
何时在 Java 中使用脏标记模式
- 当操作是资源密集型且仅在发生某些更改后才需要时。
- 在检查更改比执行操作本身便宜得多的情况下,提高成本效益。
- 在对象维护更新成本高且更新不频繁的 state 的系统中,提高性能效率。
脏标记模式 Java 教程
Java 中脏标记模式的现实世界应用
- 图形渲染引擎仅更新场景中已更改的部分,利用脏标记模式进行高效渲染。
- Web 应用程序用于部分页面渲染或缓存策略。
- 数据库应用程序用于跟踪数据集中的更改以最大程度地减少写入操作,确保有效的数据库管理。
脏标记模式的优点和权衡
优点
- 通过避免不必要的操作来减少计算和资源开销,从而提高性能。
- 在操作成本高且更改不频繁的系统中可以显着提高性能,促进系统优化。
- 简化了有关何时执行特定操作的决策过程,有助于有效地分配资源。
权衡
- 通过向系统添加 state 管理职责来引入复杂性。
- 需要仔细管理标志以确保它准确地反映 state 更改,避免陈旧或不正确的数据。
- 可能会增加与标志重置不当相关的错误风险,从而影响系统可靠性。
相关的 Java 设计模式
- 观察者:可以与之结合使用以在设置或清除脏标记时通知相关方。
- 备忘录:用于存储对象的先前 state,可以与脏标记逻辑协同工作以恢复到干净的 state。
- 命令:命令可以在执行时设置脏标记,指示需要关注的 state 更改。