轻松学习多线程-02-Single Threaded Execution 模式

2023-10-22 06:32

本文主要是介绍轻松学习多线程-02-Single Threaded Execution 模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Single Threaded Execution

这座桥,一次只能过一个人。

情景引入

使用程序模拟三个人频繁通过一个只允许通过一个人的门。
每次有人通过,人数统计便会增加。
每次通过,都会校验通过者的信息。

普通方式

定义

  • Gate.java

定义接口。

/*** 接口* @author bbhou*/
public interface Gate {/*** 对过门的人通过校验* @param name 姓名* @param address 地址*/void pass(String name, String address);}
  • UserThread.java

用户执行线程

/*** 用户线程* @author bbhou* @since 1.0.0*/
public class UserThread extends Thread {private final Gate gate;/*** 名称*/private final String name;/*** 地址*/private final String address;public UserThread(Gate gate, String name, String address) {this.gate = gate;this.name = name;this.address = address;}@Overridepublic void run() {System.out.println(this.name + " BEGIN!");while (true) {this.gate.pass(name, address);}}}
  • UnsafeGate.java

线程不安全的实现

/*** UnsafeGate 线程不安全* @since 1.0.0* @author bbhou*/
public class UnsafeGate implements Gate {/*** 计数器*/private int counter = 0;/*** 姓名*/private String name;/*** 地址*/private String address;/*** 通过* @param name 姓名* @param address 地址*/@Overridepublic void pass(String name, String address) {this.counter++;this.name = name;this.address = address;check();}/*** 信息校验*/private void check() {if(name.charAt(0) != address.charAt(0)) {System.out.println("-----------------------BROKEN-----------------------"+toString());}}@Overridepublic String toString() {return "UnsafeGate{" +"counter=" + counter +", name='" + name + '\'' +", address='" + address + '\'' +'}';}}

运行 & 测试

  • run
public static void main(String[] args) {Gate gate = new UnsafeGate();new UserThread(gate, "Apple", "Apple").start();new UserThread(gate, "Big", "Big").start();new UserThread(gate, "Cat", "Cat").start();
}
  • 运行结果
Apple BEGIN!
Cat BEGIN!
-----------------------BROKEN-----------------------UnsafeGate{counter=3393, name='Apple', address='Cat'}
Big BEGIN!
-----------------------BROKEN-----------------------UnsafeGate{counter=3903, name='Apple', address='Apple'}
-----------------------BROKEN-----------------------UnsafeGate{counter=4098, name='Apple', address='Apple'}
-----------------------BROKEN-----------------------UnsafeGate{counter=4301, name='Apple', address='Apple'}
-----------------------BROKEN-----------------------UnsafeGate{counter=3393, name='Apple', address='Cat'}
(以下省略)

结果分析

很明显,这不太符合我们的预期。
当多线程执行时,这个类是线程不安全的。
出现这种现象的原因是,当一个线程执行 check() 方法时,其他线程在执行 pass() 方法。导致 name、address 的属性被修改。
如何解决这个问题呢?
请往下看。

模式案例

  • SafeGate.java

我们将 pass()toString() 进行同步保护。如下:

/*** 线程安全* @since 1.0.0* @author bbhou*/
public class SafeGate implements Gate {/*** 计数器*/private int counter = 0;/*** 姓名*/private String name;/*** 地址*/private String address;/*** 通过* @param name 姓名* @param address 地址*/@Overridepublic synchronized void pass(String name, String address) {this.counter++;this.name = name;this.address = address;check();}/*** 信息校验*/private void check() {if(name.charAt(0) != address.charAt(0)) {System.out.println("-----------------------BROKEN-----------------------"+toString());}}@Overridepublic synchronized String toString() {return "SafeGate{" +"counter=" + counter +", name='" + name + '\'' +", address='" + address + '\'' +'}';}
}

运行 & 测试

  • run
public static void main(String[] args) {Gate gate = new SafeGate();new UserThread(gate, "Apple", "Apple").start();new UserThread(gate, "Big", "Big").start();new UserThread(gate, "Cat", "Cat").start();
}
  • 运行结果
Big BEGIN!
Apple BEGIN!
Cat BEGIN!

结果分析

这次结果不会再出现错误的信息。
原因是什么呢?

  • synchronized 的作用
    第一个案例中提到,之所以出现问题,是因为 pass() 被个线程交错执行导致的。
    synchronized 可以保证此方法同时只能被一个线程执行。

  • synchronized 保护着什么?
    本案例中,pass() 被声明为 synchronized 之后,保护着 Gate 类中的 counter,name,address 三个字段。
    确保这些字段不会被多个线程同时访问。

  • 其他地方保护好了吗?
    上面的方法中,你应该发现 check() 方法并没有被声明为 synchronized。
    会存在问题吗?
    其实不会,原因如下:
    (1)此类为 private 方法,外部无法直接访问。
    (2)调用此类的方法 pass()是被声明为 synchronized 的。
    所需该类无需进行声明。
    当然就算加上也不算错,但是这样可能会降低性能。

UML & Code

  • UML
    类之间的关系:
    UML

  • Code

代码地址

系列导航

多线程系列导航

这篇关于轻松学习多线程-02-Single Threaded Execution 模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/259709

相关文章

Python轻松实现Word到Markdown的转换

《Python轻松实现Word到Markdown的转换》在文档管理、内容发布等场景中,将Word转换为Markdown格式是常见需求,本文将介绍如何使用FreeSpire.DocforPython实现... 目录一、工具简介二、核心转换实现1. 基础单文件转换2. 批量转换Word文件三、工具特性分析优点局

Go语言实现桥接模式

《Go语言实现桥接模式》桥接模式是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立地变化,本文就来介绍一下了Go语言实现桥接模式,感兴趣的可以了解一下... 目录简介核心概念为什么使用桥接模式?应用场景案例分析步骤一:定义实现接口步骤二:创建具体实现类步骤三:定义抽象类步骤四:创建扩展抽象类步

C++多线程开发环境配置方法

《C++多线程开发环境配置方法》文章详细介绍了如何在Windows上安装MinGW-w64和VSCode,并配置环境变量和编译任务,使用VSCode创建一个C++多线程测试项目,并通过配置tasks.... 目录下载安装 MinGW-w64下载安装VS code创建测试项目配置编译任务创建 tasks.js

Java轻松实现在Excel中插入、提取或删除文本框

《Java轻松实现在Excel中插入、提取或删除文本框》在日常的Java开发中,我们经常需要与Excel文件打交道,当涉及到Excel中的文本框时,许多开发者可能会感到棘手,下面我们就来看看如何使用J... 目录Java操作Excel文本框的实战指南1. 插入Excel文本框2. 提取Excel文本框内容3

C++中的解释器模式实例详解

《C++中的解释器模式实例详解》这篇文章总结了C++标准库中的算法分类,还介绍了sort和stable_sort的区别,以及remove和erase的结合使用,结合实例代码给大家介绍的非常详细,感兴趣... 目录1、非修改序列算法1.1 find 和 find_if1.2 count 和 count_if1

Redis中群集三种模式的实现

《Redis中群集三种模式的实现》Redis群集有三种模式,分别是主从同步/复制、哨兵模式、Cluster,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1. Redis三种模式概述2、Redis 主从复制2.1 主从复制的作用2.2 主从复制流程2

深入理解MySQL流模式

《深入理解MySQL流模式》MySQL的Binlog流模式是一种实时读取二进制日志的技术,允许下游系统几乎无延迟地获取数据库变更事件,适用于需要极低延迟复制的场景,感兴趣的可以了解一下... 目录核心概念一句话总结1. 背景知识:什么是 Binlog?2. 传统方式 vs. 流模式传统文件方式 (非流式)流

Java轻松实现PDF转换为PDF/A的示例代码

《Java轻松实现PDF转换为PDF/A的示例代码》本文将深入探讨Java环境下,如何利用专业工具将PDF转换为PDF/A格式,为数字文档的永续保存提供可靠方案,文中的示例代码讲解详细,感兴趣的小伙伴... 目录为什么需要将PDF转换为PDF/A使用Spire.PDF for Java进行转换前的准备通过

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性