美文网首页
Java的单例模式种类

Java的单例模式种类

作者: Asen_十足坏蛋 | 来源:发表于2017-10-11 16:27 被阅读0次

    第一种:饿汉模式

    代码
    public class Hungry {
        private Hungry() {
    
        }
    
        private volatile static Hungry singleton = new Hungry();
    
        public static Hungry getSingleton() {
            return singleton;
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Hungry.getSingleton());
                    }
                }).start();
            }
        }
    }
    
    执行结果
    Hungry@334f2b2a
    Hungry@334f2b2a
    Hungry@334f2b2a
    Hungry@334f2b2a
    Hungry@334f2b2a
    Hungry@334f2b2a
    Hungry@334f2b2a
    Hungry@334f2b2a
    Hungry@334f2b2a
    Hungry@334f2b2a
    
    分析

    实例变量的hashCode值一致,说明对象是同一个,饿汉式单例实现是线程安全的,缺点是该类加载的时候就会直接new一个静态对象出来,当系统中这样的类较多时,会使得启动速度变慢 ,且不符合懒加载思想。

    第二种:懒汉模式

    代码
    public class Lazy {
        private Lazy() {
    
        }
    
        private volatile static Lazy singleton = null;
    
        public static Lazy getSingleton() {
            if(singleton==null) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                singleton = new Lazy();
            }
            return singleton;
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Lazy.getSingleton());
                    }
                }).start();
            }
        }
    }
    
    执行结果
    Lazy@3f3b0d5f
    Lazy@152b4efb
    Lazy@39531411
    Lazy@6517209b
    Lazy@2f8d3330
    Lazy@4e38bc80
    Lazy@3f3b0d5f
    Lazy@3f3b0d5f
    Lazy@3f3b0d5f
    Lazy@3f3b0d5f
    
    分析

    实例变量的hashCode值不一致,说明对象不是同一个,懒汉式单例实现是非线程安全的。
    优点是实现了懒加载思想。

    第三种:使用synchronized关键字的懒汉式

    代码
    public class SyncMethodLazy {
        private SyncMethodLazy() {
    
        }
    
        private static SyncMethodLazy singleton = null;
    
        public static synchronized SyncMethodLazy getSingleton() {
            if(singleton==null) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                singleton = new SyncMethodLazy();
            }
            return singleton;
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(SyncMethodLazy.getSingleton());
                    }
                }).start();
            }
        }
    }
    
    执行结果
    SyncMethodLazy@3f3b0d5f
    SyncMethodLazy@3f3b0d5f
    SyncMethodLazy@3f3b0d5f
    SyncMethodLazy@3f3b0d5f
    SyncMethodLazy@3f3b0d5f
    SyncMethodLazy@3f3b0d5f
    SyncMethodLazy@3f3b0d5f
    SyncMethodLazy@3f3b0d5f
    SyncMethodLazy@3f3b0d5f
    SyncMethodLazy@3f3b0d5f
    
    分析

    实例变量的hashCode值一致,说明对象是同一个,使用synchronized关键字的懒汉式单例实现是线程安全的。
    优点是实现了线程安全并且是懒加载的;缺点是在同一时刻getSingleton方法只能由一个线程访问,效率会很低。

    第四种:使用synchronized关键字修饰全部代码块的懒汉式

    代码
    public class SyncFullCodeBlockLazy {
        private SyncFullCodeBlockLazy() {
    
        }
    
        private static SyncFullCodeBlockLazy singleton = null;
    
        public static SyncFullCodeBlockLazy getSingleton() {
            synchronized (SyncFullCodeBlockLazy.class) {
                if(singleton==null) {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    singleton = new SyncFullCodeBlockLazy();
                }
                return singleton;
            }
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(SyncFullCodeBlockLazy.getSingleton());
                    }
                }).start();
            }
        }
    }
    
    执行结果
    SyncFullCodeBlockLazy@152b4efb
    SyncFullCodeBlockLazy@152b4efb
    SyncFullCodeBlockLazy@152b4efb
    SyncFullCodeBlockLazy@152b4efb
    SyncFullCodeBlockLazy@152b4efb
    SyncFullCodeBlockLazy@152b4efb
    SyncFullCodeBlockLazy@152b4efb
    SyncFullCodeBlockLazy@152b4efb
    SyncFullCodeBlockLazy@152b4efb
    SyncFullCodeBlockLazy@152b4efb
    
    分析

    实例变量的hashCode值一致,说明对象是同一个,使用synchronized关键字修饰全部代码块的懒汉式单例实现是线程安全的。
    这种实现方式其实和synchronized修饰方法的实现方式优缺点一致。

    第五种:使用synchronized关键字修饰局部代码块的懒汉式

    代码
    public class SyncPartCodeBlockLazy {
        private SyncPartCodeBlockLazy() {
    
        }
    
        private static SyncPartCodeBlockLazy singleton = null;
    
        public static SyncPartCodeBlockLazy getSingleton() {
            if(singleton==null) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (SyncPartCodeBlockLazy.class) {
                    singleton = new SyncPartCodeBlockLazy();
                }
            }
            return singleton;
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(SyncPartCodeBlockLazy.getSingleton());
                    }
                }).start();
            }
    
        }
    }
    
    执行结果
    SyncPartCodeBlockLazy@2f8d3330
    SyncPartCodeBlockLazy@3abbf4ae
    SyncPartCodeBlockLazy@39531411
    SyncPartCodeBlockLazy@17ea7521
    SyncPartCodeBlockLazy@1667513b
    SyncPartCodeBlockLazy@6517209b
    SyncPartCodeBlockLazy@4e38bc80
    SyncPartCodeBlockLazy@152b4efb
    SyncPartCodeBlockLazy@6fcaf925
    SyncPartCodeBlockLazy@75809226
    
    分析

    实例变量的hashCode值不一致,说明对象不是同一个,使用synchronized关键字修饰局部代码块的懒汉式单例实现是非线程安全的。
    虽然这种实现方法相较于第三种和第四种方式效率要高一些,但并非线程安全的。

    第六种:使用synchronized关键字双重检查的懒汉式

    代码
    public class SyncDoubleCheckLazy {
        private SyncDoubleCheckLazy() {
    
        }
    
        private static SyncDoubleCheckLazy singleton = null;
    
        public static SyncDoubleCheckLazy getSingleton() {
            if(singleton==null) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (SyncDoubleCheckLazy.class) {
                    if(singleton==null) {
                        singleton = new SyncDoubleCheckLazy();
                    }
                }
            }
            return singleton;
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(SyncDoubleCheckLazy.getSingleton());
                    }
                }).start();
            }
        }
    }
    
    执行结果
    SyncDoubleCheckLazy@75809226
    SyncDoubleCheckLazy@75809226
    SyncDoubleCheckLazy@75809226
    SyncDoubleCheckLazy@75809226
    SyncDoubleCheckLazy@75809226
    SyncDoubleCheckLazy@75809226
    SyncDoubleCheckLazy@75809226
    SyncDoubleCheckLazy@75809226
    SyncDoubleCheckLazy@75809226
    SyncDoubleCheckLazy@75809226
    
    分析

    实例变量的hashCode值不一致,说明对象不是同一个,使用synchronized关键字双重检查的懒汉式单例实现是线程安全的。
    其实这种方式是综合了第三、四、五这三种实现方式,即实现了线程安全,也相对提高了运行效率,值得推荐。

    第七种:使用静态内部类的方式

    代码
    public class StaticInnerClass {
        private StaticInnerClass() {
    
        }
    
        private static class StaticInnerClassProvider{  
            private static StaticInnerClass singleton = new StaticInnerClass();  
        }
    
        public static StaticInnerClass getSingleton() {
            return StaticInnerClassProvider.singleton;
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(StaticInnerClass.getSingleton());
                    }
                }).start();
            }
        }
    }
    
    执行结果
    StaticInnerClass@6517209b
    StaticInnerClass@6517209b
    StaticInnerClass@6517209b
    StaticInnerClass@6517209b
    StaticInnerClass@6517209b
    StaticInnerClass@6517209b
    StaticInnerClass@6517209b
    StaticInnerClass@6517209b
    StaticInnerClass@6517209b
    StaticInnerClass@6517209b
    
    分析

    实例变量的hashCode值不一致,说明对象不是同一个,使用静态内部类方式的单例实现是线程安全的。
    虽然咋看上去这种方式和第一种饿汉模式的单例模式一样,两者都是采用了类装载的机制来保证初始化实例时只有一个线程,但是有区别的地方在于静态内部类方式在StaticInnerClass类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载StaticInnerClassProvider类,从而完成StaticInnerClass的实例化,所以这种方式也值得推荐。

    相关文章

      网友评论

          本文标题:Java的单例模式种类

          本文链接:https://www.haomeiwen.com/subject/kjvwyxtx.html