Singleton - 图1

单例的实现

基于预初始化且线程安全的单例模式

  1. package online.javabook.gof.creational.patterns6.singleton;
  2. public class NotLazyAndThreadSafeSingleton {
  3. private static NotLazyAndThreadSafeSingleton instance = new NotLazyAndThreadSafeSingleton();
  4. private NotLazyAndThreadSafeSingleton(){
  5. }
  6. public static NotLazyAndThreadSafeSingleton getInstance() {
  7. return instance;
  8. }
  9. }

基于懒加载且线程不安全的单例模式(单线程下可用)

  1. package online.javabook.gof.creational.patterns6.singleton;
  2. public class LazyButNotThreadSafeSingleton {
  3. private static LazyButNotThreadSafeSingleton instance;
  4. private LazyButNotThreadSafeSingleton(){
  5. }
  6. public static LazyButNotThreadSafeSingleton getInstance() {
  7. if (instance == null) {
  8. instance = new LazyButNotThreadSafeSingleton();
  9. }
  10. return instance;
  11. }
  12. }

基于同步懒加载且线程安全的单例模式(但是性能被synchronized影响)

  1. package online.javabook.gof.creational.patterns6.singleton;
  2. public class LazyAndThreadSafeSingleton {
  3. private static LazyAndThreadSafeSingleton instance;
  4. private LazyAndThreadSafeSingleton(){
  5. }
  6. public static synchronized LazyAndThreadSafeSingleton getInstance() {
  7. if (instance == null) {
  8. instance = new LazyAndThreadSafeSingleton();
  9. }
  10. return instance;
  11. }
  12. }

基于基于双重检查的单例模式

  1. package online.javabook.gof.creational.patterns6.singleton;
  2. public class DoubleCheckedLockingSingleton {
  3. private volatile static DoubleCheckedLockingSingleton singleton;
  4. private DoubleCheckedLockingSingleton(){
  5. }
  6. public static DoubleCheckedLockingSingleton getSingleton() {
  7. if (singleton == null) {
  8. synchronized (DoubleCheckedLockingSingleton.class) {
  9. if (singleton == null) {
  10. singleton = new DoubleCheckedLockingSingleton();
  11. }
  12. }
  13. }
  14. return singleton;
  15. }
  16. }

基于静态内部类的单例模式

  1. package online.javabook.gof.creational.patterns6.singleton;
  2. public class StaticInnerClassSingleton {
  3. private static class SingletonHolder {
  4. private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
  5. }
  6. private StaticInnerClassSingleton(){
  7. }
  8. public static final StaticInnerClassSingleton getInstance() {
  9. return SingletonHolder.INSTANCE;
  10. }
  11. }

静态代码块的同步访问例子

JVM保证类的 方法中的代码在多线程下会被同步锁定,只能被一个线程执行

  1. package online.javabook.jvm.classinit.syncstaticblock;
  2. public class ClassWithStaticBlock {
  3. static {
  4. System.out.println("被线程:" + Thread.currentThread() + "加载中");
  5. try {
  6. Thread.sleep(10000);
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. System.out.println("被线程:" + Thread.currentThread() + "加载结束");
  11. }
  12. }
  1. package online.javabook.jvm.classinit.syncstaticblock;
  2. public class SyncLoadStaticBlockTest {
  3. public static void main(String[] args) {
  4. Thread thread1 = new Thread("线程1") {
  5. @Override
  6. public void run() {
  7. new ClassWithStaticBlock();
  8. }
  9. };
  10. Thread thread2 = new Thread("线程1") {
  11. @Override
  12. public void run() {
  13. new ClassWithStaticBlock();
  14. }
  15. };
  16. thread1.start();
  17. thread2.start();
  18. }
  19. }
  1. # 说明:静态块中类加载时只能被一个线程同步初始化
  2. 被线程:Thread[线程1,5,main]加载中
  3. 被线程:Thread[线程1,5,main]加载结束

对单例的序列化和反序列化

如果一个原本期望设计成单例的对象,即便没有public的构造函数,使用了预创建对象,或者线程安全的延迟创建对象。但是如果对象本身是一个可以序列化的对象,则会绕过这些制约,导致对象经过序列化和反序列化后创建出新的实例。

package online.javabook.design.gof.creational6.singleton;

import com.sun.corba.se.impl.orbutil.ObjectUtility;
import online.javabook.design.gof.creational5.prototype.graph.palette.product.LineGraphPrototype;

import java.io.*;

public class LazyAndReadResolveAndThreadSafeSingleton implements Serializable {

    private static LazyAndReadResolveAndThreadSafeSingleton instance =  new LazyAndReadResolveAndThreadSafeSingleton();

    private LazyAndReadResolveAndThreadSafeSingleton(){

    }

    public static synchronized LazyAndReadResolveAndThreadSafeSingleton getInstance() {
        return instance;
    }

    /*private Object readResolve() {
        return instance;
    }*/

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        LazyAndReadResolveAndThreadSafeSingleton instance1 = LazyAndReadResolveAndThreadSafeSingleton.getInstance();
        System.out.println("instance1:"+instance1);

        // out
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(instance1);

        // in
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        LazyAndReadResolveAndThreadSafeSingleton instance2 = (LazyAndReadResolveAndThreadSafeSingleton) ois.readObject();

        oos.close();
        ois.close();

        System.out.println("instance2:"+instance2);
    }
}

经过序列化和反序列化出来的实例已经是一个新的对象了。它绕过了原本单例的初衷。


instance1:online.javabook.design.gof.creational6.singleton.LazyAndReadResolveAndThreadSafeSingleton@29453f44
instance2:online.javabook.design.gof.creational6.singleton.LazyAndReadResolveAndThreadSafeSingleton@4769b07b

为了避免这个问题,可以在添加readResolve方法强制返回instance实例,避免反序列化后返回新的对象。重新执行代码,会发现即便通过序列化和反序列化也仍然会返回同样的对象实例。

虽然如此,这样做容易把事情弄的太过于复杂,在java开发中经常会采用约定的编程方式,例如约定这个类会是单例,哪怕构造函数是public的,如果已经有框架来托管对象的创建,就不要考虑自己再实例化出一个新对象了。


instance1:online.javabook.design.gof.creational6.singleton.LazyAndReadResolveAndThreadSafeSingleton@29453f44
instance2:online.javabook.design.gof.creational6.singleton.LazyAndReadResolveAndThreadSafeSingleton@29453f44

现实世界中的单例

Runtime.getRuntime();

预初始化的单例在现实世界中是最常用的,没有那么多花里胡哨。那为什么会有这么多花里胡哨的实现呢,之前说过设计模式很多都是来自客户端程序鼎盛的时期,我猜测是因为一些基于插件机制的架构,一个本地应用存在很多插件实例,但是中实际的使用过程中,用户未必会每个子插件都会点开使用,这个时候延迟加载就派上用场了。但是中web中即便是插件机制,作为一个非单机,非单用户的系统,很少会有哪个模块不会被触发。

package java.lang;

import java.io.*;
import java.util.StringTokenizer;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;

/**
 * Every Java application has a single instance of class
 * <code>Runtime</code> that allows the application to interface with
 * the environment in which the application is running. The current
 * runtime can be obtained from the <code>getRuntime</code> method.
 * <p>
 * An application cannot create its own instance of this class.
 *
 * @author  unascribed
 * @see     java.lang.Runtime#getRuntime()
 * @since   JDK1.0
 */

public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}