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

相关文章

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

RabbitMQ工作模式中的RPC通信模式详解

《RabbitMQ工作模式中的RPC通信模式详解》在RabbitMQ中,RPC模式通过消息队列实现远程调用功能,这篇文章给大家介绍RabbitMQ工作模式之RPC通信模式,感兴趣的朋友一起看看吧... 目录RPC通信模式概述工作流程代码案例引入依赖常量类编写客户端代码编写服务端代码RPC通信模式概述在R

电脑蓝牙连不上怎么办? 5 招教你轻松修复Mac蓝牙连接问题的技巧

《电脑蓝牙连不上怎么办?5招教你轻松修复Mac蓝牙连接问题的技巧》蓝牙连接问题是一些Mac用户经常遇到的常见问题之一,在本文章中,我们将提供一些有用的提示和技巧,帮助您解决可能出现的蓝牙连接问... 蓝牙作为一种流行的无线技术,已经成为我们连接各种设备的重要工具。在 MAC 上,你可以根据自己的需求,轻松地

python多线程并发测试过程

《python多线程并发测试过程》:本文主要介绍python多线程并发测试过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、并发与并行?二、同步与异步的概念?三、线程与进程的区别?需求1:多线程执行不同任务需求2:多线程执行相同任务总结一、并发与并行?1、

SQL Server身份验证模式步骤和示例代码

《SQLServer身份验证模式步骤和示例代码》SQLServer是一个广泛使用的关系数据库管理系统,通常使用两种身份验证模式:Windows身份验证和SQLServer身份验证,本文将详细介绍身份... 目录身份验证方式的概念更改身份验证方式的步骤方法一:使用SQL Server Management S