一、Single Threaded Execution 模式介绍
简单的来说,Single threaded execution 模式描述了在一种多线程环境下各个线程对于公用资源的使用方式——任一时刻,只有一个线程可以使用该公用资源~
STE有三个关键部分:多线程、公有资源、唯一线程可使用;关于为什么使用多线程这个问题,答案是我们可以利用它来获得好处,这个好处就是我们系统的一部分性能将获得提高,虽然系统整体性能不一定提高,但是总是有可能的嘛。如果系统中,某一资源可以有很多,大家各自用各自的,相安无事,即没有产生公用~但是如果某些资源对系统来说负担过于沉重(比如涉及到IO、数据库连接等费时操作时,我们还是希望花一份时间比较好),或者这样的资源只能有一个(比如,一个网站的访问次数统计器,每个页面都需要使用到它,如果各自使用各自的,那么统计的就是网页访问次数了,还比如一些全局配置文件,当该文件发生变化后,系统响应部分需要得到同步更新,各自使用一个该文件肯定就不行了,还有就是唯一序列号的生成器等应用),那么就需要公用这里资源了。总体来说,STE这里的公用资源具有“不得不”只有一个或者“最好”只有一个以及”某一时刻,只能一个线程使用”的特点~
至于为什么某一时刻只能一个线程使用公用资源,这是因为某些时候多条线程同时访问公用资源(1)(想一想,什么是“同时访问”呢,如果只有一个CPU,那么某一时刻,一定只有一个线程使用公用资源,但这是STE吗?)会对公用资源造成破坏,使其失去使用价值。比如A打算把X(=10)加1后打印,理论上来说A输出X=11;但如果A把X加1后还没来得及打印就被置换出去了,B开始执行把X加1,然后输出,理论上B输出X=12,结果B的确输出X=12,这时A又被调度执行,输出的就不再是X=11,而是X=12;即B对X的访问,破坏了A对X的访问。嗯,我认为,同时访问就是指一个线程A对公用资源的一套处理流程没有全部执行完,另一个线程B就开始了对公用资源的处理。(这里再问个问题,就是在(1)处某些时候的对立面的情况下,我们还有必要使用STE吗?即是不是只要有一个公用资源,我们就必须使用STE模式使用多线程呢?其实,第一段中的定义还是比较准确的,因为STE只描述了一种方式哦~哈哈哈,算是小提示啦,同时提醒自己!);
二、Single Threaded Execution的使用场景
- 多线程编程环境
- 多个线程同时访问共享资源;
- 共享资源的状态会因线程的访问而改变;(换言之,如果共享资源的状态不会因为多线程访问而改变,那么就不适用STE模式啦);
三、Single Threaded Execution的实例代码及分析
// 多线程类,功能及时让来自address的那么通过gate类
public class WorkerThread extends Thread {
private final Gate gate;
private final String name;
private final String address;
public WorkerThread(Gate g,String n,String a){
this.gate=g;
this.name=n;
this.address=a;
}
public void run(){
System.out.println(name+" Begin");
while(true){
gate.pass(name,address);
}
}
}
//Gate 抽象类,我们将派生出线程安全的Gate和非线程安全的Gate
public abstract class Gate {
int counter=0;
String name="Nobody";
String address="Nowhere";
public abstract void pass(String name,String address);
}
//安全的Gate,嗯,不防盗,但是防多线程不安全访问~
public class SafeGate extends Gate {
public synchronized String toString(){//这是一个同步方法哦
return "No."+counter+": "+name+","+address;
}
void check(){//这里不用同步,因为该函数的调用者已经同步过啦
if(name.charAt(0)!=address.charAt(0)){
System.out.println("******BROKEN****** "+toString());
}
}
@Override
public synchronized void pass(String name, String address) {//安全的同步门
super.counter++;
super.name=name;
super.address=address;
check();
}
}
//非线程安全的Gate
public class UnSafeGate extends Gate {
public void pass(String name,String address){
super.counter++;
super.name=name;
super.address=address;
check();
}//该函数的执行可能被打断,而造成公用数据被修改
public String toString(){
System.out.println("Father toString");
return "No."+counter+": "+name+","+address;
}
private void check(){
if(name.charAt(0)!=address.charAt(0)){
System.out.println("******BROKEN****** "+toString());
}
}
}
四、对Single Threaded Execution的理解
在面向对象程序设计领域中,大师们总结出著名的23种设计模式,江湖上自然还有若干非著名(但不一定不实用哦)的设计模式,其中单例模式就位于著名的23种当中;
当我看到STE时,感觉和设计模式中单例模式非常像,嗯,其实他们的偏重点不同,理念也不同!相同的是涉及的资源都只有一个,然而,这不是重点啊~
两者的不同之处就在于:
- 使用环境不同:如果在单线程环境下,我们仍然可以使用单例模式以保证系统各个部分使用的是同一个对象。而STE的大环境条件就是多线程啊~
- 侧重点不同:单例模式更偏向如何创建唯一一个对象~而对该对象的使用环境设定也多为单线程,即没有考虑多线程环境下访问该对象时面临的安全性问题;STE模式更偏向如何在多线程环境下安全地使用一个共享对象,至于该类的对象是否唯一并不关注。
总体来说,STE指出了多线程环境下对可变共享资源的一种访问方式,是一种多线程环境下利用互斥访问以实现线程安全的策略。
当然,具体的应用中还需要灵活地结合实际的功能需求,不过,同单例模式搭配使用的STE,味道应该很不错哦~(单例模式保证该对象全局唯一,STE保证对该对象的访问线程安全~);