我们首先看一个简单的,Runtime这个类在java.lang下的,我们看一下getRuntime()
public static Runtime getRuntime() {
return currentRuntime;
}
可以看到这里直接返回一个currentRuntime
private static Runtime currentRuntime = new Runtime();
我们可以看到这个对象是static,并且在类加载的时候就已经创建出来了,属于饿汉式,非常简单,我们再看一个Desktop,
awt的,只不过现在用JAVAD写服务端比较多一点,但是这个也分领域,某些也用JAVA写桌面程序非常多,打个比方,Eclipse就是
用JAVA写的,还是非常强大的,那我们看一下,我们看一下getDesktop这个方法
public static synchronized Desktop getDesktop(){
if (GraphicsEnvironment.isHeadless()) throw new HeadlessException();
if (!Desktop.isDesktopSupported()) {
throw new UnsupportedOperationException("Desktop API is not " +
"supported on the current platform");
}
sun.awt.AppContext context = sun.awt.AppContext.getAppContext();
Desktop desktop = (Desktop)context.get(Desktop.class);
if (desktop == null) {
desktop = new Desktop();
context.put(Desktop.class, desktop);
}
return desktop;
}
这个方法关注的,synchronized同步方法,获取桌面对象,同步方法,最后返回,那我们看一下这里面AppContext,这个是一个App的上下文,
然后下边做了各种判断,判断这个对象是否为空,如果是空的话就new一个,放进去,那我们来看一下这个对象context,这里面的put做了
什么呢
首先这里有this.table,点开table看一下,它是一个HashMap类型
这些判断先忽略,看重点,然后给这个map上了个锁,下面就开始put了,向HashMap里面放值,那我们再退回来,
public static synchronized Desktop getDesktop(){
if (GraphicsEnvironment.isHeadless()) throw new HeadlessException();
if (!Desktop.isDesktopSupported()) {
throw new UnsupportedOperationException("Desktop API is not " +
"supported on the current platform");
}
sun.awt.AppContext context = sun.awt.AppContext.getAppContext();
Desktop desktop = (Desktop)context.get(Desktop.class);
if (desktop == null) {
desktop = new Desktop();
context.put(Desktop.class, desktop);
}
return desktop;
}
这里面同步方法,sun.awt.AppContext context = sun.awt.AppContext.getAppContext();然后看一下这行,
这个从上下文里面,去获取这个类型的对象,然后下面又有空判断,如果为空就往里面put,最后就返回,那这里讲的
就非常像我们之前讲的容器单例,可以找到容器单例的一个影子,只不过这里加了各种同步的控制,包括put的时候,
还有这个方法调用的时候,来保证这一块是线程安全的,那讲完这些我们再想一下,那Spring中的单例,和我们写的这个
单例还不太一样,那Spring的单例呢,是基于容器的,打个比方,如果我写一个主函数,或者一个应用,启动了多个容器,
那在每个容器中都能够拿到这个单例对象,也就是说呢,Spring里面的单例模式和我们讲的单例模式,不是一个,
那Spring中的单例呢,是bean作用域中的一个,这个作用域在每个应用上下文中,仅创建一个,我们设置单例属性的这个实例,
那和我们的设计模式最大区别是什么呢,Spring将实例的数量,作用域在整个应用程序的上下文,而我们这个单例模式呢,
而我们写的这种单例模式,在JAVA应用程序中,是将这些实例的数量,限制在类加载器管理的整个空间里,所以刚刚也说了,
Spring启动容器的时候,每个容器即使是单例的,我都可以拿出来这个对象,那其实在Spring中,能找到一些单例的影子,
你看这个类,AbstractFactoryBean,我们找一个方法,getObject方法
我们可以看一下这里面做了一些判断,也就是说判断这个对象如果是单例的,就return,如果不是单例的就直接创建instance,
createInstance(),从这个名字就能够看出来,然后看一下这里面的语法,一个三元,是否被初始化呢,如果已经被初始化了,
直接返回,如果没有被初始化,调用getEarlySingletonInstance(),获取早期的单例对象,我们来看一下
如果单例对象是空的话,通过代理去获取这个对象,然后进行强转,把这个对象赋值上,然后对他进行返回,现在我们再看一个
关于Mybatis当中,使用单例模式的一些解决方案,我们打开他,public class ErrorContext,错误的上下文,那首先看到的
就是这个private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();
首先我们看一下他的构造器
private ErrorContext() {
}
他的构造器是私有的,然后看一下instance
public static ErrorContext instance() {
ErrorContext context = LOCAL.get();
if (context == null) {
context = new ErrorContext();
LOCAL.set(context);
}
return context;
}
这里就基于ThreadLocal来使用了,而这个ThreadLocal呢,又是一个私有的静态的final的
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();
然后我们先不说这个,if (context == null) 我们先看一下这里,如果这个上下文是空的话,就创建一个新的,创建完成之后,
把它放到ThreadLocal里边,如果这个线程下次来的话,肯定能够get出来,然后就直接返回了,那Mybatis这里面的错误上下文,
也就是说他保证了每个线程,各自的数据,不同的线程,每个线程自己的错误上下文,自己保存好,就像前面所讲的,在线程隔离方面,
是非常有益处的,基于ThreadLocal的这种单例模式,我们也找到了,只不过他不是并不是整个应用全局唯一,而是在线程里是唯一的,
那单例模式的源码呢,就分析到这里,希望在单例模式这里有所收获,然后强调一点,如果问设计模式的话,喜欢问单例模式来挖掘深度,
希望能把我们所讲的这些单例模式,实现方案,优缺点,应用场景,及其在源码中的一些使用,还有比如序列化的破坏,反射攻击防御等等,
这些都能理解透