轻松学习多线程-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

相关文章

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

macOS无效Launchpad图标轻松删除的4 种实用方法

《macOS无效Launchpad图标轻松删除的4种实用方法》mac中不在appstore上下载的应用经常在删除后它的图标还残留在launchpad中,并且长按图标也不会出现删除符号,下面解决这个问... 在 MACOS 上,Launchpad(也就是「启动台」)是一个便捷的 App 启动工具。但有时候,应

SpringBoot如何通过Map实现策略模式

《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

Java使用多线程处理未知任务数的方案介绍

《Java使用多线程处理未知任务数的方案介绍》这篇文章主要为大家详细介绍了Java如何使用多线程实现处理未知任务数,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 知道任务个数,你可以定义好线程数规则,生成线程数去跑代码说明:1.虚拟线程池:使用 Executors.newVir

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

JAVA封装多线程实现的方式及原理

《JAVA封装多线程实现的方式及原理》:本文主要介绍Java中封装多线程的原理和常见方式,通过封装可以简化多线程的使用,提高安全性,并增强代码的可维护性和可扩展性,需要的朋友可以参考下... 目录前言一、封装的目标二、常见的封装方式及原理总结前言在 Java 中,封装多线程的原理主要围绕着将多线程相关的操

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx

大数据spark3.5安装部署之local模式详解

《大数据spark3.5安装部署之local模式详解》本文介绍了如何在本地模式下安装和配置Spark,并展示了如何使用SparkShell进行基本的数据处理操作,同时,还介绍了如何通过Spark-su... 目录下载上传解压配置jdk解压配置环境变量启动查看交互操作命令行提交应用spark,一个数据处理框架

Python中多线程和多进程的基本用法详解

《Python中多线程和多进程的基本用法详解》这篇文章介绍了Python中多线程和多进程的相关知识,包括并发编程的优势,多线程和多进程的概念、适用场景、示例代码,线程池和进程池的使用,以及如何选择合适... 目录引言一、并发编程的主要优势二、python的多线程(Threading)1. 什么是多线程?2.