单例模式3要素“

    1.构造私有,防止外部直接new对象

    2.定义静态成员类变量

    3.提供静态方法

 

饿汉式:

public class SingleTon{
    private static SingleTon singleTon = new SingleTon();
    private SingleTon(){}
    public static SingleTon getSingleTon(){
        return singleTon;
    }
}

优点:

     1.线程安全,因为类加载的时候已经完成了单例创建

     2.因此获取单例的时候不需要加锁

 缺点:

      单例的创建不是延迟加载,无论你用与否,都已经被加载,是不是有点浪费资源?

懒汉式:

public class SingleTon{
    private static SingleTon singleTon = null;
    private SingleTon(){}
    public synchronized static SingleTon getSingleTon(){
        if(singleTon==null) singleTon = new SingleTon();
        return singleTon;
    }
}

优点:

     1.延迟加载,没用到时不加载

     2.为了线程安全,我们在获取实例的方法添加了synchronized

缺点:

     1.getSingleTon方法为synchronized,无论单例是否已经创建,所有来获取单例的线程都得排队等待,调用频繁的话撞锁严重

 

双重检测:

 该方案对于懒汉式进一步改进,对象存在时直接获取 无需上锁,只有对象不存在需要创建时才进行加锁

public class SingleTon{
    private static SingleTon singleTon = null;
    private SingleTon(){}
    public static SingleTon getSingleTon(){
        if(singleTon==null){ //对象不存在时再创建
            synchronized(SingleTon.class){  //对象创建时对该类加锁
                if(singleTon==null) {  //再次校验是否被创建
                    singleTon = new SingleTon();
                }
            }
        }
        return singleTon;
    }
}

优点:

     1.延迟加载,节省资源

     2.线程安全

     3.撞锁几率小,只有初次创建才会有撞锁可能

问题:

你上面的方案能防止reflection构造对象吗? 不能,如何解决呢?

首先对于reflection构造对象的问题,只需要在默认构造函数在对象存在的情况下被再次调用时抛出异常

public class SingleTon{
    private static SingleTon singleTon = null;
    private SingleTon(){
        if(singleTon!=null)
           throw new Exception("the instance already be created!");
    }
    public static SingleTon getSingleTon(){
        if(singleTon==null){ //对象不存在时再创建
            synchronized(SingleTon.class){  //对象创建时对该类加锁
                if(singleTon==null) {  //再次校验是否被创建
                    singleTon = new SingleTon();
                }
            }
        }
        return singleTon;
    }
}

其次能防止反序列化构造对象吗?不能,如何解决呢? 重写readResolve方法!

public class SingleTon{
    private static SingleTon singleTon = null;
    private SingleTon(){
        if(singleTon!=null)
           throw new Exception("the instance already be created!");
    }
    public static SingleTon getSingleTon(){
        if(singleTon==null){ //对象不存在时再创建
            synchronized(SingleTon.class){  //对象创建时对该类加锁
                if(singleTon==null) {  //再次校验是否被创建
                    singleTon = new SingleTon();
                }
            }
        }
        return singleTon;
    }
//这个方法会在反序列化时调用
protected Object readResolve(){ return getSingleTon(); } }

 

4.静态内部类

java 再创建外部类的时候不会创建内部类实例,只有在外部类使用到内部类的时候才会创建内部类实例

public class SingleTon{
    private SingleTon(){}
    private static class SingleTonInner{
         private static final instance = new SingleTon();
    }
    public static SingleTon getInstance(){
         return SingleTonInner.instance;
    }
}

难道静态内部类创建就是线程安全的吗?为啥这个时候不需要加synchronized? 这个要涉及到java虚拟机的工作原理,菜鸟暂时不做过多研究。总之多个线程在同时进行静态内部类创建的时候,虚拟机会保证只有一个线程可以执行cinit方法,其他线程都阻塞等待。

 5.枚举

还有一种方式就是枚举

public enum SingleTon{
    INSTANCE  
}

 

那究竟什么时候用到单例呢?

1.网站计数器

2.数据库连接池

3.日志组件

4.多线程的线程池