单例模式(Singleton Pattern)

 

单例模式(Singleton Pattern)

确保一个类在任何情况下都绝对只有一个实例,并且提供一个全局访问点。有时候创建对象时耗时过多或者耗资源过多,但是我们经常用这个对象。我们就可以使用单例模式,并且如果一个工具类存有状态,我们也需要单例,因为如果是多个实例,可能存在数据读取错误的问题,例如配置类,我们就需要单例它,大家去一个地方取就可以了,不需要多个创建。常见的写法

  • 饿汉式单例
  • 懒汉式单例
  • 注册式单例
  • Threadlocal单例

To explain some terms

High execution efficiency:

  • classes is  created ,When classes are been loading, if we want to get  it just getting  it and without to create it again

Thread safety:

  • caused  by the calsses that you want are already created,if you do not create it again, it is thread safety,isn't it!
  • there are no  situation that two threads get an one method that you use to creat classes.

Waste of memory:

  • who can sure that the calsses are uesd certainly?
  • if you don't use these classes, which means these are occupying memory.

Eager Singleton

public class HungrySingleton {
    private static final HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton() { }

    private HungrySingleton getInstance() {
        return hungrySingleton;
    }
}

advantages:1.High execution efficiency 2.Thread safety

disadvantages:1.Waste of memory

To solve the problem of Waste of memory. . . . 

Lazy Singleton

public class LazySimpleSingleton {
    private static LazySimpleSingleton lazySimpleSingleton;

    private LazySimpleSingleton() { }

    public static LazySimpleSingleton getInstance() {
        if (lazySimpleSingleton != null) {
            lazySimpleSingleton = new LazySimpleSingleton();
        }
        return lazySimpleSingleton;
    }
}

 To test

public class ExecuteThread implements Runnable {
    public void run() {
        LazySimpleSingleton lazySimpleSingleton=LazySimpleSingleton.getInstance();
        System.out.println(Thread.currentThread().getName() + ":" + lazySimpleSingleton);
    }
}
public class LazySimpleSingletonTest {
    public static void main(String[] args) {
        Thread thread=new Thread(new ExecuteThread());
        Thread thread2=new Thread(new ExecuteThread());
        thread.start();
        thread2.start();
    }
}

result:

Actually,Sometimes it produces the same results, sometimes it doesn't,which shows There are problem of security of  Thread!

same results:

  • normal execution sequence
  • The latter covers the former(2 Threads execute the same code,the former assign the classes but did not return,at this time,the latter began to execute,so latter covers the former.in fact, there are two classes were created )

different results:

  •     2 Threads execute the same code,the former have returned,the the latter returned

tips: the red one is what i said the same code executed

      if (lazySimpleSingleton != null) {
            lazySimpleSingleton = new LazySimpleSingleton();
        }

advantages: Save memory

disadvantages: Thread unsafe 

  To solve the problem of Thread of unsafe. . . . (easily,To put a key word of synchronized in front of "static LazySimpleSingleton" like ->(

  public synchronized static LazySimpleSingleton getInstance())

),but cause a problem that all of the others threads are waiting, the picture  below shows that  if  one thread get a source,which means others can not get the same source,all others will be blocked,in this way,all of others will be waiting and one by one to  execute

 

 

 

 

  To solve the problem of performance--------------------------->

 

public class LazyDoubleCheckSingleton {
    private static LazyDoubleCheckSingleton lazySimpleSingleton;

    private LazyDoubleCheckSingleton() {
    }

    public static LazyDoubleCheckSingleton getInstance() {
        // To check whether we should to create or not,if already the class is create, the left of others do not need to execute code below(solve problem of performance)
        if (lazySimpleSingleton == null) {
            synchronized (LazyDoubleCheckSingleton.class) {
                // if two threads enter into, what should we do,so Let's make a judgment here
                if (lazySimpleSingleton == null) {
                    lazySimpleSingleton = new LazyDoubleCheckSingleton();
                }
            }
        }
        return lazySimpleSingleton;
    }
}

 

To explain:

first of all,all threads can be allowed to enter into the method, and if  lazySimpleSingleton == null the threads can enter into(solve the problem of performance->do not need to wait), then to use 'synchronized ' to limit  problem of Thread unsafe,but if  more than one thread enter into  and one of these has executed into ending of block of 'if', definitely, the other still will created,so we make a judgement again(resolve the problem of thread unsafe )

but !!! the method will not be elegant enough.. it is hard to read,and produce problem 'Tag and Seek',so we add 'volatile' in front of 'static LazyDoubleCheckSingleton' like

 

private volatile static LazyDoubleCheckSingleton lazySimpleSingleton;

 

  To solve the problem of not enough elegant--------------------------->

public class LazyStaticInnerClassSingleton {
    public LazyStaticInnerClassSingleton() {
        // what it is essential to creating the class is to use  Constructor to create
        // so if someone wants to use Constructor to create,we just throw Exception to avoid illegal create  
        throw new RuntimeException("forbid to create....");
    }

    public LazyStaticInnerClassSingleton getInstance() {
        return LazyHolder.lazyStaticInnerClassSingleton;
    }

    private static class LazyHolder {
        private static final LazyStaticInnerClassSingleton lazyStaticInnerClassSingleton = new LazyStaticInnerClassSingleton();
    }
}

 To explain:

apparently!we use  mechanism of class loading,it is well known that during the classess the inner classess are not loaded,when we get the inner class that we want,than the inner class that we want will be load! but all of methods above will be destory by reflect!! what the essence that  to use the reflect to create the classess is to use Constructor!so we just need to  throw new RuntimeException in Constructor  to avoid illegal create , but what i do still not enough decent,who konw the reason why we thrown an exception at the Constructor? 

  To solve the problem of not enough decent---------------------------> 

Register Singleton(container Singleton)

public enum  EnumSingleton {
    INSTANCE;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public   Object data;

    public static EnumSingleton getInstance(){
        return INSTANCE;
    }

}

public class User {
    private String userName;
}

To Test

public class EnumSingletonTest {
    public static void main(String[] args) {
        EnumSingleton instance = EnumSingleton.getInstance();
        instance.setData(new User());
        Class<EnumSingleton> enumSingletonClass = EnumSingleton.class;
        try {
            Constructor<EnumSingleton> constructor = enumSingletonClass.getDeclaredConstructor(String.class,int.class);
            constructor.setAccessible(true);
            EnumSingleton enumSingleton = constructor.newInstance();
            System.out.println(enumSingleton);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

To explain:

the image illustrate that we are not able to create a class with reflection

 

we find the class of Constructor has wrote ' if the modifier is emum than the class can not be create'

 

 

 

 

 

but, why we say that the method must be Thread safety,we still can skim the souce code->(Enum#valueOf),as soon as we create the Enum the  'INSTANCE ' will be put in the container,so Thread safety

 

 

 

 

 

 

 so, in some extent , it will produce a new problem of Waste of memory,(put these INSTANCE  into container in advance)

 

  To solve the problem of not Waste of memory---------------------------> (we can simluate do it as what IOC does)

public class ContainerSingleton {

    ContainerSingleton() {
    }

    private static Map<String, Object> iocMock = new ConcurrentHashMap<String, Object>();

    public static Object getInstance(String className) {
        Object instance = null;
        if (!iocMock.containsKey(className)) {
            try {
                instance = Class.forName(className).newInstance();
                iocMock.put(className, instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return instance;
        }
        return iocMock.get(className);
    }
}
public class Pojo {
    String mock;
}

To test

public class ContainerSingletonTest {
    public static void main(String[] args) {
        Object pojo = ContainerSingleton.getInstance("Pojo");
        Object pojo1 = ContainerSingleton.getInstance("Pojo");
        System.out.println(pojo==pojo1);
    }
}

actually,  serialization can destory singleton!!!!!

  To solve the problem of destruction the singleton with serialization --------------------------->

 

public class SerializableSingleton implements Serializable {
    final static SerializableSingleton INSTANCE = new SerializableSingleton();

    private SerializableSingleton() {
    }


    public static SerializableSingleton getInstance() {
        return INSTANCE;
    }

    private Object readResolve() {
        return INSTANCE;
    }
}

To test 

public class SerializableSingletonTest {
    public static void main(String[] args) {

        SerializableSingleton s1 = null;

        SerializableSingleton s2 = SerializableSingleton.getInstance();

        FileOutputStream fos = null;
        try {

            fos = new FileOutputStream("D:\\growingup\\growup\\target\\SerializableSingleton.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            // output the file into disk
            oos.writeObject(s2);
            oos.flush();
            oos.close();

            //input the file in  disk into  memory
            FileInputStream fis = new FileInputStream("D:\\growingup\\growup\\target\\SerializableSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            s1 = (SerializableSingleton)ois.readObject();
            ois.close();

            System.out.println(s1);
            System.out.println(s2);
            System.out.println(s1 == s2);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

To  explain:

first we  output the file into disk and input the file in disk into memory again,the result is true(still is a same class)the key is #readResolve,we still skim the souce of code(code java.io.ObjectInputStream#readObject0 

 

 

 after we have read can get that information of there is a judgment if you  have a  method named readResolve then to get with reflection, otherwise,to create a new instance.so we must be set a method called readResolve,which can help us to resolve problem of serialization  with souce of code of java.lang.ClassNotFoundException

 The last pattern of singleton i give

Threadlocal  pattern of singleton(be ensure globally unique in the same Thread )

public class ThreadLocalSingleton {
    static final ThreadLocal<ThreadLocalSingleton> threadLocalSingletonThreadLocal = new ThreadLocal<ThreadLocalSingleton>() {
        @Override
        protected ThreadLocalSingleton initialValue() {
            return new ThreadLocalSingleton();
        }
    };

    private ThreadLocalSingleton() {
    }

    public static ThreadLocalSingleton getInstance() {
        return threadLocalSingletonThreadLocal.get();
    }

}
public class ExecuteThreadLocal implements  Runnable {
    public void run() {
        ThreadLocalSingleton threadLocalSingleton=ThreadLocalSingleton.getInstance();
        System.out.println(Thread.currentThread().getName() + ":" + threadLocalSingleton);
    }
}

To test

public class ThreadLocalSingletonTest {
    public static void main(String[] args) {
        System.out.println(ThreadLocalSingleton.getInstance());
        System.out.println(ThreadLocalSingleton.getInstance());
        System.out.println(ThreadLocalSingleton.getInstance());

        Thread thread=new Thread(new ExecuteThreadLocal());
        Thread thread2=new Thread(new ExecuteThreadLocal());
        thread.start();
        thread2.start();
    }
}

 

 

 To explain

the image above shows there are different threads get different results,but we can get a same instance in same thread,example is main thread(we get  the same result in 3 times),we can find reason with resource of code of java.lang.ThreadLocal#get.

Store on a per-thread basis

practical application in souce of code

  •  org.springframework.beans.factory.config.AbstractFactoryBean#getObject
  •  org.apache.ibatis.executor.ErrorContext#instance

 Sum up

advantages:

  •  ther is just one instance in memory,which is efficient to diminish  Memory overhead
  • Access is effectively controlled

disasvantages: 

there is no interface,so that it is difficult to expand(if we want to extend function,we have  to modify original code)

 

 

posted @ 2021-04-12 17:21  UpGx  阅读(122)  评论(0编辑  收藏  举报