本文主要是介绍轻松学习多线程-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
类之间的关系:
Code
代码地址
系列导航
多线程系列导航
这篇关于轻松学习多线程-02-Single Threaded Execution 模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!