JAVA单例模式

来源:互联网 发布:科比详细身体数据 编辑:程序博客网 时间:2024/06/11 00:03

最近在看《设计模式教程》,看到单例模式的例子,虽然以前就已经接触过单例模式,但是现在在看到又有了新的感受。

以前总是在笔记本上做笔记,但是现在看起来还是在网上记录比较方便,毕竟更快捷。

单例模式的出现主要是应对于在程序设计中避免一个全局使用的类被频繁地创建,节省系统资源花销

使用场景:1.要求产生唯一序列号。

2.web计算中的计算器,不用每次刷新都在数据库里加一次,用单例先缓存起来。

3.创建的一个对象需要消耗的资源太多,比如I/O与数据库的连接。

注意事项:getInstance()方法需要使用同步锁synchronized(Singleton.class)防止多线程同时进入造成instance被多次实例化。


步骤1 创建Singleton类

public class SimgleObject {

//创建一个SingleObject 的一个对象

public static SingleObject instance = new SingleObject();

//让构造函数私有化,这样的类不会被实例化

private SingleObject(){}

//获取唯一可用对象

public static Singleton getInstance(){

return instance;

}

public void showMessage() {

System.out.println("hello world");

}

}

步骤2 从Singleton 类获取唯一对象

public class SingletonPatternDemo {

public static void main(String[] args) {

//SingleObject object = new SingleObject();

//不合法的构造函数,编译时报错:构造函数SingleObject()是不可见的

//获取唯一可用的对象

Singleton object = SingleObject.getInstance();

object.showMessage();

}

}


几种实现方式

1.懒汉式,线程不安全

是否延迟初始化:是

是否多线程安全:否

->最容易实现的方式,最大问题是不支持多线程。因为没有加锁synchronized,所以严格意义不算单例模式。

public class Singleton {

private Singleton instance;

private Singleton(){}

public static Singleton getInstance(){

if(instance == null) {

instance = new Singleton();

}

return instance;

}

}


2.懒汉式,支持多线程

是否延迟初始化:是

是否多线程安全:是

->具备很好的lazy loading能够在多线程中很好的工作,但是效率很低,99%情况下不需要同步。

优点:第一次调用初始化,避免内存浪费。

缺点:必须加锁以保证单例,加锁影响效率。


public class Singleton {

private Singleton instance;

private Singleton(){}

public static synchronized Singleton getInstance(){

if(instance == null) {

instance = new Singleton();

}

return instance;

}

}


3.饿汉式

是否Lazy初始化:否

是否线程安全:是

->次方法比较常用且容易实现,但容易产生垃圾对象

优点:没有加锁,执行效率会提高

缺点:类加载就初始化,浪费内存

基于classloader机制避免多线程的同步问题,不过instance在类加载时候就初始化,虽然导致类装载的原因有很多,在单例模式中大都是通过调用getInstance方式,但是也不能确定其他方式(或者其他的静态方法)导致类装载,这时候初始化没达到lazy loading的效果。


public class Singleton {

private Singleton instance = new Singleton();

private Singleton(){}

public static Singleton getInstance(){

return instance;

}

}


4.双重锁/双重校验锁(DCL:double-checked locking)

是否lazy初始化:是

是否多线程安全:是

->使用双锁机制,安全且在多线程情况下能保持高性能。getInstance性能对应用程序很关键。

public class Singleton {

private volatile static Singleton singleton;

private Singleton(){}

public static Singleton getInstance() {

if(singleton == null ) {

synchronnzied(Singleton.class) {

if(instance==null) {

singleton = new Singleton();

}

}

}

return singleton;

}

}


5.登记式/静态内部类

是否Lazy初始化:是

是否多线程安全:是

->此方法与双检锁方式一样的功效,但实现更简单,对静态域使用延迟初始化,应使用这种方式而不是双检锁。此方式只适用于静态域的情况,双检锁可在实例域需要延迟初始化时使用。

这种方式同样利用了classloader机制来保证初始化instance时只有一个线程,它跟方式3不同的是 方式3只要singleton类被装载,那么instance就被实例化,没达到延时加载的效果,而这种方式子类被装载时,Instance不一定被实例化。因为SingletonHolder没有被主动使用,只用调用getInstance方法,才会显示装载SingletonHolder类,实例化instance。若实例化instance很消耗资源,所以想让她延时加载,另一方面,又不希望在Singleton类在加载时就实例化,因为不能确保Singleton类还可能在其它地方被主动使用而被加载,那么这个时候实例化instance显然是不合适。这个时候这种方式相比方式3就显得很合理。

public class Singleton{

private static class SingletonHolder{

private static final Singleton INSTANCE = new Singleton();

}

private Singleton(){}

public static final Singleton getInstance(){

return SingletonHolder.INSTANCE;

}

}


6.枚举

是否lazy初始化:否

是否多线程安全:是

实现难度:易

->这种实现方式还没被广泛采用,但这是单例模式的最佳实现方式。它更简洁,自动支持序列化机制,绝对防止多次实例化。

public enum Singleton {

INSTANCE;

public void whatebverMethod(){}

}

一般情况下,不建议使用第一第二懒汉的方式,建议使用第三种恶汉式。只有在明确要实现延时加载效果时才会使用第五种方法。如果涉及反序列化创建对象时,可以尝试第六种枚举方法。如有其他特殊需求,可以考虑使用第四种双检锁方式。


0 0
原创粉丝点击