zoukankan      html  css  js  c++  java
  • 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;

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

  • 相关阅读:
    Educational Codeforces Round 86 (Rated for Div. 2) D. Multiple Testcases
    Educational Codeforces Round 86 (Rated for Div. 2) C. Yet Another Counting Problem
    HDU
    HDU
    HDU
    HDU
    Good Bye 2019 C. Make Good (异或的使用)
    Educational Codeforces Round 78 (Rated for Div. 2) C. Berry Jam
    codeforces 909C. Python Indentation
    codeforces1054 C. Candies Distribution
  • 原文地址:https://www.cnblogs.com/yzjT-mac/p/5882655.html
Copyright © 2011-2022 走看看