重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
这篇文章主要讲解了“java单例模式实现的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java单例模式实现的方法是什么”吧!
柳城网站建设公司创新互联,柳城网站设计制作,有大型网站制作公司丰富经验。已为柳城1000多家提供企业网站建设服务。企业网站搭建\外贸网站制作要多少钱,请找那个售后服务好的柳城做网站的公司定做!
许多人都了解,单例的创建大致可以分为两种:
饱汉型
饿汉型
所谓饿汉型,就是单例的对象,我上来就先创建好了,什么时候用直接拿就好了。例如这样的方式:
private static final Singleton singleton = new Singleton();public static Singleton getInstance() {
return singleton;
}
这样的实现方式没有任何问题。问题出在哪呢?
有些时候,要实现Singleton的对象比较大,或者创建比较耗资源,耗时等,我们希望能在需要的时候再初始化,而不放在class 加载的时候,也就是实现所谓的lazy load。
这个时候问题就来了,这种所谓的饱汉型要怎么写呢?
你可能见到过这种形式的实现
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
这样的实现,有问题么?是不是感觉棒棒的?
在单线程的环境中跑的话,这样也是可以保证只返回一个instance的。
问题出在多线程环境下执行。
多个线程执行时,极有可能两个线程同时执行到判断是否为null的情况,又同时创建了实例出来。为了解决多线程的问题,你毫不犹豫的给方法加上了synchronized,兵不血刃的解决了问题。
问题又来了!
当这个方法使用很频繁的时候,synchronized带来的互斥效果,导致每次只能一个线程执行,效率很低。
此时,一个聪明的想法浮现在脑海(当然,可能是查资料,网上浏览了解到的)。使用双重锁检查(Double lock checking)来提高效率,实现起来是这个样子:
public static Single getInstance() {
if(singleton == null) {
synchronized(this) {
if(singleton == null) {
singleton = new Singletoon();
}
}
}
return singleton;
}
我们的方法并不是互斥的,只有在instance为空时才会加锁检查。看似无懈可击!
这个时候有一个问题,是看似普通的new XXX这种操作,其本质上和i++
这种操作一样,并不是一个原子操作。例如,我们下面这几行代码:
public class Test {
private int i = 5;
private int a = 2;
public Test(int i, int a) {
this.i = i;
this.a = a;
}
public void ttt() {
Test t = new Test(1,1); //普通的实例化一个对象
}
}
大致包含的步骤有:
创建对象
初始化对象的各个域,为其赋值
将对象指向其引用
但是,对于这些指令的执行,却并不一定是按照这个顺序执行,为了执行效率,这些指令会被优化,指令被重新排序。极有可能对象被创建后即指向了其引用,但各个域并没有初始化,如果此时被使用,那拿到的就是一个构造不完整的对象。(可以参考Java并发编程实战了解对象逃逸)
为了使代码不被优化影响,Java 5在修订了Java内存模型(JMM)之后,可以使用volatile声明,不允许指令重排序。
volatile关键字同时保证了内存的有序性和可见性,保证程序可以按照预期执行。所以,要实现一个正确无误的DCL单例,需要同时把singleton对象声明为
volatile,这一定很重要。
如果不使用DCL,我们还有其它方式实现延迟初始化。例如下面这种内部类的形式,也是比较常用的。
public class Foo {
private static class FooHolder {
static final Foo foo = new Foo();
}
public static Foo getFoo() {
return FooHolder.foo;
}
}
由于内部类只有在使用时才会初始化,所以保证了单例的延迟初始化。
了解了以上这些后,我们来看Tomcat中的单例,是如何使用的。
首先我们来看看Tomcat中对于DCL的使用。
/** Whether the servlet needs reloading on next access */
private volatile boolean reload = true;
public Servlet getServlet() throws ServletException {
// DCL on 'reload' requires that 'reload' be volatile
// (this also forces a read memory barrier, ensuring the
// new servlet object is read consistently)
if (reload) {
synchronized (this) {
// Synchronizing on jsw enables simultaneous loading
// of different pages, but not the same page.
if (reload) {
// This is to maintain the original protocol.
destroy();
final Servlet servlet;
servlet.init(config);
reload = false;
// Volatile 'reload' forces in order write of 'theServlet' and new servlet object
} } }
return theServlet;
}
上面的代码是关于jsp对应的Servlet获取时对应的代码,其中对于DCL的使用主要用于判断jsp文件对应的class是否需要重新加载。(jsp文件工作原理前面文章介绍过,感兴趣的朋友可以看JSP文件修改实时生效的秘密)
单例的使用,Tomcat中的方式很简单,
public final class ApplicationFilterFactory {
private static ApplicationFilterFactory factory = null;
private ApplicationFilterFactory() {
// Prevent instantiation outside of the getInstanceMethod().
}/**
* Return the factory instance.
*/
public static ApplicationFilterFactory getInstance() {
if (factory == null) {
factory = new ApplicationFilterFactory();
}
return factory;
}
我们看到和前面第一次提到的饱汉型一样,没有使用DCL,也没加
synchronized,这是因为在Tomcat中对于此处ApplicationFactory的使用,只有在StandardWrapperValve启动才会触发其初始化,并不会涉及到多线程环境的使用,所以可以放心使用这种方式。
看到这里的朋友,其实单例还有一种实现方式,是Effective Java的作者推荐使用的,使用起来更简单,只需要一个枚举项的enum即可,之后可以包含其对应的各个方法:
public enum Singleton {
INSTANCE;
public void test() {
System.out.println("test");
}
}
而我们使用的时候,直接这样使用即可:
Singleton.INSTANCE.test();
感谢各位的阅读,以上就是“java单例模式实现的方法是什么”的内容了,经过本文的学习后,相信大家对java单例模式实现的方法是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!