码迷,mamicode.com
首页 > 其他好文 > 详细

单例模式

时间:2020-06-20 22:14:31      阅读:69      评论:0      收藏:0      [点我收藏+]

标签:存储   err   懒汉式   java   style   get   ice   span   oid   

 

1. 

  解决问题:一个全局使用的对象要频繁的创建和销毁

  实现单例模式的要素:①类只有一个实例 ②由类自行创建这个实例  ③提供一个获取这个实例的方法

  实现:①类提供静态成员存储此单例对象  ②构造私有   ③提供get方法获取单例对象

 

1.1 饿汉式

  ①静态变量--类加载时就初始化了这个变量

package com.sjms;

public class Singleton {
    private static final Singleton simple = new Singleton();
    private Singleton(){}
    public static Singleton getInstance() {
        return simple;
    }
}

 

  测试:

ExecutorService es = Executors.newCachedThreadPool();
for(int i=0;i<30;i++) {
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            Singleton s = Singleton.getInstance();
            System.out.println(s);
        }
    });
    es.execute(thread);
}
es.shutdown();

 

  ②静态代码块初始化

package com.sjms;

public class Singletonv2 {
    private static Singletonv2 simple;
private Singletonv2(){}
static { simple = new Singletonv2(); } public static Singletonv2 getInstance() { return simple; } }

 

  测试:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestSJMS {
    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();
        for(int i=0;i<30;i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {                 
                    Singletonv2 s = Singletonv2.getInstance();
                    System.out.println(s);
                }
            });
            es.execute(thread);
        }
        es.shutdown();
    }
}

  静态代码块只在类初次加载时调用,因此只会创建一份

 

  多个线程反射调用试试看:

package com.sjms;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestSJMS {
    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();
        for(int i=0;i<30;i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Class aClass = Class.forName("com.sjms.Singletonv2");
                        Method getInstance = aClass.getMethod("getInstance");
                        System.out.println(getInstance.invoke(null));
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
//                    Singletonv2 s = Singletonv2.getInstance();
//                    System.out.println(s);
                }
            });
            es.execute(thread);
        }
        es.shutdown();
    }
}

  

  饿汉式的问题:类加载就创建单例对象,如果这个对象一直没有用到,此单例对象还是会创建

 

1.2 懒汉式

  类加载时不创建,获取时才创建---延迟创建

  多线程不安全:会创建多个对象,不是单例了

package com.sjms;

public class Singleton2 {
    private static Singleton2 simple;
    private Singleton2(){}
    public static Singleton2 getInstance() {
        if(simple == null) {
            simple = new Singleton2();
        }
        return simple;
    }
}

 

  测试:

package com.sjms;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestSJMS {
    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();
        for(int i=0;i<30;i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    Singleton2 s = Singleton2.getInstance();
                    System.out.println(s);
                }
            });
            es.execute(thread);
        }
        es.shutdown();
    }
}

  测试结果:创建了多个对象

    技术图片

 

 

(2)懒汉式-->对获取方法加锁

  上面方法的问题:假设两个线程,线程A刚判断simple为空间,准备去创建对象,此时simple还是为null;线程B也判断simple为null,于是也去创建了一个对象

  解决:对获取方法加锁---解决了问题,但效率也低了很多

package com.sjms;
/*懒汉式:对获取方法加锁*/
public class Simpletom2v2 {
    private static Simpletom2v2 simple;
    private Simpletom2v2(){}
    public synchronized static Simpletom2v2 getInstance(){
        if(simple == null) {
            simple = new Simpletom2v2();
        }
        return simple;
    }
}

 

(3)懒汉式-->双检锁

  上面对整个方法加锁,效率较低  

package com.sjms;

public class Simpleton2v3 {
    private static Simpleton2v3 simple;
    private Simpleton2v3(){}
    public static Simpleton2v3 getInstance() {
        if(simple == null) {
            synchronized (Simpleton2v3.class) {
                if(simple == null) {
                    simple = new Simpleton2v3();
                }
            }
        }
        return simple;
    }
}

 

  问题:由于JVM的指令重排序,线程A先进入锁住的代码块创建对象,但还未创建完成;线程B进入第一个判断,simple不为空,于是直接返回simple,如果线程B接着立刻对这个单例对象进行使用,会造成无法预知的错误。

  这里的指令重排序:线程A:①分配内存空间 ②在这块内存上创建Simpleton2v3实例  ③将simple引用指向这块内存

         本来的步骤应该是这样,但由于②、③没有依赖关系,可能会使③先于②执行,造成线程B使用了不完整的实例对象

  问题即:创建对象不是原子操作

 

  问题解决:volatile 禁止指令重排序,即对 simple 加 volatile 修饰

package com.sjms;
/*懒汉式:双检锁*/
public class Simpleton2v3 {
//    private static Simpleton2v3 simple;
    private volatile static Simpleton2v3 simple;
    private Simpleton2v3(){}
    public static Simpleton2v3 getInstance() {
        if(simple == null) {
            synchronized (Simpleton2v3.class) {
                if(simple == null) {
                    simple = new Simpleton2v3();
                }
            }
        }
        return simple;
    }
}

 

 

  1.3 静态内部类实现

  综合饿汉式的 静态成员(不用锁) 以及 懒汉式的获取时才创建

  效率高、线程安全、可延迟加载

package com.sjms;
/*静态内部类实现:线程安全、效率高、可延迟加载*/
public class Simpleton3 {
    private Simpleton3(){}

    private static class SimpletonClass{
        private static Simpleton3 instance = new Simpleton3();
    }

    public static Simpleton3 getInstance() {
        return SimpletonClass.instance;
    }
}

 

  1.4 枚举

public enum  Simpleton4 {
    INSTANCEA;
}

 

enum Simpleton5 {
        INSTANCEA,
        INSTANCEB
    }

 

  枚举的使用:

 

单例模式

标签:存储   err   懒汉式   java   style   get   ice   span   oid   

原文地址:https://www.cnblogs.com/taoXiang/p/13170230.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!