• Singleton


    1.//懒汉模式
    //天生线程不安全,但是效率高
    public class Singleton {
        private static Singleton singleton;
        private  Singleton() {}
        public static  Singleton getSingleton(){
            if(singleton==null){
                singleton=new Singleton();
                System.out.println("实例化Singleton");
            }
            return singleton;
        }
        
    }
    
    public class Test {
        public static void main(String[] args) {
            MyThread myThread1=new MyThread();
            MyThread myThread2=new MyThread();
            MyThread myThread3=new MyThread();
            myThread1.start();
            myThread2.start();
            myThread3.start();
        }
        //因为继承Thread每次都会新创建一个任务,所以可以达到效果,而使用Runnable不能够达到效果
        public static class MyThread extends Thread{
            int count=0;
            @Override
            public void run() {
                while(count<100){
                    count++;
                    Singleton.getSingleton();
                }
            }
        }
    }

    此程序输出的结果为:

    实例化Singleton
    实例化Singleton

    说明此时实例化了两个Singleton,线程不安全!

    2.在上面做一点改动

    //改良版懒汉模式
    //线程安全,但是效率低
    public class Singleton {
        private static Singleton singleton;
        private  Singleton() {}
        public static  synchronized Singleton getSingleton(){
            if(singleton==null){
                singleton=new Singleton();
                System.out.println("实例化Singleton");
            }
            return singleton;
        }
        
    }
    
    public class Test {
        public static void main(String[] args) {
            MyThread myThread1=new MyThread();
            MyThread myThread2=new MyThread();
            MyThread myThread3=new MyThread();
            myThread1.start();
            myThread2.start();
            myThread3.start();
        }
        //因为继承Thread每次都会新创建一个任务,所以可以达到效果,而使用Runnable不能够达到效果
        public static class MyThread extends Thread{
            int count=0;
            @Override
            public void run() {
                while(count<100){
                    count++;
                    Singleton.getSingleton();
                }
            }
        }
    }

    3.饿汉模式

    //饿汉模式
    //线程安全,但每次加载类的时候都会实例化,若存在大量的此种单例模式,则会实例化很多无用实例
    public class Singleton {
        private static Singleton singleton=new Singleton();
        private  Singleton() {}
        public static Singleton getSingleton(){
            System.out.println("Singleton");
            return singleton;
        }
    }

    4.饿汉模式

    //饿汉模式,和第三种一样,只不过换个写法
    
    public class Singleton {
        private static Singleton singleton = null;
        static {
            singleton = new Singleton();
        }
    
        private Singleton() {
        }
    
        public static Singleton getSingleton() {
            return singleton;
        }
    }

    5.静态内部类

    //这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):
    第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。
    因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。
    想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,
    因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。
    public
    class Singleton { public static class SingletonHolder{ private static final SingletonHolder singletonHolder=new SingletonHolder(); private SingletonHolder(){}; public static SingletonHolder getSingletonHolder(){ return singletonHolder; } } }

     6.枚举类(推荐)

     public enum Singleton { singleton; //此处可以是任何方法 public void out(){ System.out.println(singleton.hashCode()); } } 

    使用枚举类作为单例的好处有三点:1.天生线程安全(因为枚举类是线程安全的)2.反序列化后不会创建多个实例 3.防反射攻击(因为枚举类是abstract),反编译如下

    public abstract class Singleton extends Enum
    {
    
        private Singleton(String s, int i)
        {
            super(s, i);
        }
    
        protected abstract void read();
    
        protected abstract void write();
    
        public static Singleton[] values()
        {
            Singleton asingleton[];
            int i;
            Singleton asingleton1[];
            System.arraycopy(asingleton = ENUM$VALUES, 0, asingleton1 = new Singleton[i = asingleton.length], 0, i);
            return asingleton1;
        }
    
        public static Singleton valueOf(String s)
        {
            return (Singleton)Enum.valueOf(singleton/Singleton, s);
        }
    
        Singleton(String s, int i, Singleton singleton)
        {
            this(s, i);
        }
    
        public static final Singleton INSTANCE;
        private static final Singleton ENUM$VALUES[];
    
        static 
        {
            INSTANCE = new Singleton("INSTANCE", 0) {
    
                protected void read()
                {
                    System.out.println("read");
                }
    
                protected void write()
                {
                    System.out.println("write");
                }
    
            };
            ENUM$VALUES = (new Singleton[] {
                INSTANCE
            });
        }
    }

    7.双重检查锁定

    public class Singleton {  
        private volatile static Singleton singleton;  
        private Singleton (){}  
        public static Singleton getSingleton() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
            if (singleton == null) {  
                singleton = new Singleton();  
            }  
            }  
        }  
        return singleton;  
        }  
    }  

    总结

    有两个问题需要注意:

    1.如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。

    2.如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。

    对第一个问题修复的办法是:

    Java代码  收藏代码
    1. private static Class getClass(String classname)      
    2.                                          throws ClassNotFoundException {     
    3.       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();     
    4.       
    5.       if(classLoader == null)     
    6.          classLoader = Singleton.class.getClassLoader();     
    7.       
    8.       return (classLoader.loadClass(classname));     
    9.    }     
    10. }  

     对第二个问题修复的办法是:

    Java代码  收藏代码
    1. public class Singleton implements java.io.Serializable {     
    2.    public static Singleton INSTANCE = new Singleton();     
    3.       
    4.    protected Singleton() {     
    5.         
    6.    }     
    7.    private Object readResolve() {     
    8.             return INSTANCE;     
    9.       }    
    10. }   

    更多详情可以访问http://cantellow.iteye.com/blog/838473;

    在此感谢这篇文章给我的启示。

  • 相关阅读:
    使用Link Shell Extension方便的创建同步文件
    DOM案例【3】密码强度检查案例
    DOM案例【2】注册文本倒计时
    DOM案例【1】文本时钟
    HTML5 and CSS【01】Font
    常用单词
    CSS基础【01】类和ID选择器的区别
    【03】Html重点
    【02】Html(如鹏)
    C#MD5计算代码
  • 原文地址:https://www.cnblogs.com/yzjT-mac/p/5882655.html
Copyright © 2020-2023  润新知