单例模式

保证一个类中只有唯一的实例。


单例类图

饿汉式:浪费空间,线程安全

public class SingletonHungry {
    //类中直接创建实例
    private static SingletonHungry instance = new SingletonHungry();

    //私有化构造
    private SingletonHungry() {
    }


    //返回此类创建的实例
    public static SingletonHungry getInstance() {
        return instance;
    }
}
//测试
public class SingletonTest {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(SingletonHungry.getInstance());
            }
        }).start();
        System.out.println(SingletonHungry.getInstance());
    }
}
//结果:
com.jun.Singleton.SingletonHungry@1d44bcfa
com.jun.Singleton.SingletonHungry@1d44bcfa

懒汉式:使用的时候创建。节省空间,存在多线程安全问题

public class SingletonLazy {
    //维护一个指向本类的实例引用
    private static SingletonLazy instance = null;

    private SingletonLazy() {
    }

    public static SingletonLazy getInstance() {
        if (instance == null) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            instance = new SingletonLazy();
        }
        return instance;
    }
}
//测试
public class SingletonTest {
    public static void main(String[] args) throws Exception{
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(SingletonLazy.getInstance());
            }
        }).start();
        System.out.println(SingletonLazy.getInstance());
    }
}
//结果:得到不同的实例,出现多线程安全问题
com.jun.Singleton.SingletonLazy@1d44bcfa
com.jun.Singleton.SingletonLazy@2a8dfc9

解决懒汉式安全问题:加同步,此方式会造成效率低下。

完美解决饿汉式空间浪费,懒汉式线程安全问题的终极方法:

public class SingletonHold {
    //整一个内部类,在需要使用实例时,返回
    private static class SingletonInnerHold {
        private static SingletonHold instance = new SingletonHold();
    }

    private SingletonHold() {
        System.out.println("创建hold");
    }

    public static SingletonHold getInstance() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return SingletonInnerHold.instance;
    }
}
//测试
 public static void main(String[] args) throws Exception{
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(SingletonHold.getInstance());
            }
        }).start();
        System.out.println(SingletonHold.getInstance());
    }
//结果
创建hold
com.jun.Singleton.SingletonHold@1d44bcfa
com.jun.Singleton.SingletonHold@1d44bcfa

内部类中创建实例,在使用的时候才会创建,解决了懒汉式的空间浪费。
也不会出现线程安全问题,完美。
单例模式静态内部类为何线程安全?
虚拟机会保证一个类的类构造器<clinit>()在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的类构造器<clinit>(),其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。

特别需要注意的是,在这种情形下,其他线程虽然会被阻塞,但如果执行方法的那条线程退出后,其他线程在唤醒之后不会再次进入/执行方法,因为 在同一个类加载器下,一个类型只会被初始化一次。如果在一个类的方法中有耗时很长的操作,就可能造成多个线程阻塞,在实际应用中这种阻塞往往是隐藏的。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容