创建三个类:
类名 | 说明 |
Main | 创建一个门,让三个人不断的通过门 |
Gate | 表示门的类,穿过一个人时记录名字和出生地 |
UserThread | 表示人的类。人们不断通过门 |
public class Main {
public static void main(String[] args) {
System.out.println("Testing Gate, go ");
Gate gate = new Gate();
new UserThread(gate,"Alice","Aalska").start();
new UserThread(gate,"Bobby","Brazuk").start();
new UserThread(gate,"Chris","Canada").start();
}
}
public class Gate {
private int counter = 0;
private String name = "Nobody";
private String address = "Nowhere";
public void pass(String name,String address){
this.counter++;
this.name = name;
this.address = address;
check();
}
@Override
public String toString() {
return "Gate{" +
"counter=" + counter +
", name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
private void check() {
if (name.charAt(0) != address.charAt(0)){
System.out.println("*** BROKEN *** " + toString());
}
}
public class UserThread extends Thread {
private final Gate gate;
private final String myname;
private final String myadress;
public UserThread(Gate gate,String myname,String myadress) {
this.gate = gate;
this.myadress = myadress;
this.myname = myname;
}
public void run() {
System.out.println(myname + " Begin");
while (true){
gate.pass(myname,myadress);
}
}
}
我们的本意是名字的首字母和地址的首字母不相同时才打印BROKEN的信息,结果并不是我们想的这样,即使首字母都相同,还是会打印出来。
一个门 ,被多个人使用时,也就出现了安全隐患。
Gate类的实例被多个线程使用时,也就是说着个门是共享资源,运行结果与预期不一致,这个Gate类是不安全的,是非线程安全类。
我们先看看最主要的门的方法:
public void pass(String name,String address){
this.counter++;
this.name = name;
this.address = address;
check();
}
这个方法记录的是每个人经过时,门要记录的某个人的名字和地址。设想一下,如果两个人同时经过这个门,现在有两个名字和两个地址,而门一次性只能记一个人的信息,因此也就发生了安全隐患。要解决这个问题的根本就是一个门只能让一个人经过。
我们只要修改这个门的类的pass方法:
再运行看看:
无论多久都不会打印***BROKEN***
synchronized这个关键字修饰的方法意味着该方法同时只能由一个线程执行。也就是一个门,同时只能让一个人经过。
角色:SharedResource(共享资源)
Gate类其实就是扮演SharedResource角色,SharedResource角色是可以被多个线程调用的类,这个类有很多方法,无非两种,
第一种是多个线程调用会发生问题的方法,第二种就是多个线程调用不会发生问题的方法。
我们所要解决的就是第一种方法:通过在方法前加synchronized关键字,就起到了这个方法同时只能让一个线程调用的目的,确保程序的安全性。
1 多个线程访问时
当然,如果所有的线程都是完全独立操作的,那也无需用此模式,这种状态称为线程互不干涉。在某些处理多线程的框架中,线程的独立性是由框架内部控制的,这时使用者也就无需顾忌安全隐患,正常处理你的业务即可。
2 状态有可能发生变化时
如果在创建实例后,实例的状态再也不会发生变化,那就无需使用此模式。后续介绍的Immutable(不可变) 模式中,实例的状态不会发生变化,自然也就无需使用synchronized方法。
3 需要确保安全性时
只有在需要确保安全性时,才需要此模式。
例如Java的集合类大多数都是非线程安全的。这是为了不需要考虑安全性时提高程序的运行速度。
如果要使用安全的集合,Java提供了以下方法可确保集合类为线程安全的。
Collections是java.util.下的一个集合工具类