本文主要是介绍轻松学习多线程-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 模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!