设计模式之单例模式【内附对象实例化几种方式、实现线程安全几种方式】

继续来复习常用的设计模式-单例模式,顺便回忆一下线程安全的几种实现方式。

一、什么是单例模式

单例模式,简单常用的一种设计模式,也很好的体现了代码控制对象在内存数量的一种方式,主要分2种实现方式:

①饿汉式,线程安全

②懒汉式,线程不安全(添加锁机制,可以实现线程安全)

个人理解:顾名思义,就是单例,单个实例,在内存当中保持只有一个实例的方式。

解决问题:控制了类的实例化数量,减少了不必要的内存消耗。

二、demo演示

⑴饿汉式(线程安全):

/**
 * @param
 * @Title: Dog
 * @Description:单例模式(不提供实例化构造方法,设为私有,提供获取唯一实例的方法)
 * 饿汉模式,类加载时,就实例化对象,造成内存浪费,线程安全的,使用静态常量变量,保证实例唯一。
 * @return
 * @throws
 */
public class Hungry_dog {

    private static final Hungry_dog dog=new Hungry_dog();

    private Hungry_dog(){

    }

    public static Hungry_dog getDog(){
        return dog;
    }

    public void call(){
        System.out.println("单例模式饿汉式,线程安全");
    }
}

1.为了首次加载类时,就实例化对象,则必须将实例放在静态代码区(因为只加载一次,所以并发访问时,不存在线程安全问题)。

2.为了实现类的对象实例在内存当中唯一,不提供使用new方式进行实例化,故把构造函数设为私有化。

3.但得必须提供一个外部可以获取该实例的方法,所以必须实现一个对外开放的获取实例的方法。

⑵懒汉式[延迟/按需加载](线程不安全)

/**
 * @param
 * @Title: Dog
 * @Description:单例模式(不提供实例化构造方法,设为私有,提供获取唯一实例的方法)
 * 懒汉模式,类加载时,不实例化对象,等被调用获取对象方法时再实例化对象
 * 线程不安全
 * @return
 * @throws
 */
public class Lazy_dog{

    private static Lazy_dog lazy_dog;

    private Lazy_dog(){}

    public static Lazy_dog getLazy_dog(){
        if(lazy_dog==null){
            try {
                Class c=Class.forName("com.cheng.designpattern.singleton_mode.Lazy_dog");
                Object object=c.newInstance();
                Thread.sleep(3000);//测试线程是否安全
                lazy_dog=(Lazy_dog)object;
            }catch (Exception e){
                System.out.println("反射创建实例Lazy_dog失败");
            }
        }
        return lazy_dog;
    }

    public void call(){
        System.out.println("Lazy_dog-反射创建实例成功!");
    }

}

测试一下线程是否安全:


public class Test {

    public static void main(String[] args) {
       
        //测试线程是否安全
        int a=0;
        while (a<10){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Lazy_dog lazy_dog= Lazy_dog.getLazy_dog();
                    System.out.println(lazy_dog);
                }
            }).start();
            a++;
        }
    }
}

输出:可以看到在多线程的情况下产生了很多实例,证明确实是线程不安全

⑶懒汉式[延迟/按需加载](线程安全),最简单的处理方式,在创建实例的方法上添加同步锁

优点:线程安全

缺点:性能下降,并且把整个类的对象添加的线程锁

/**
 * @param
 * @Title: Dog
 * @Description:单例模式(不提供实例化构造方法,设为私有,提供获取唯一实例的方法)
 * 懒汉模式,类加载时,不实例化对象,等被调用获取对象方法时再实例化对象
 * 添加方法同步锁-线程安全
 * @return
 * @throws
 */
public class Lazy_dog{

    private static Lazy_dog lazy_dog;

    private Lazy_dog(){}

    public synchronized static Lazy_dog getLazy_dog(){
        if(lazy_dog==null){
            try {
                Class c=Class.forName("com.cheng.designpattern.singleton_mode.Lazy_dog");
                Object object=c.newInstance();
                Thread.sleep(3000);
                lazy_dog=(Lazy_dog)object;
            }catch (Exception e){
                System.out.println("反射创建实例Lazy_dog失败");
            }
        }
        return lazy_dog;
    }

    public void call(){
        System.out.println("Lazy_dog-反射创建实例成功!");
    }

}

输出:可以看到在多线程的情况下产生同一实例

⑷懒汉式[延迟/按需加载](线程安全),在创建实例的方法内特定的代码添加同步锁

优点:线程安全,性能较好

缺点:对性能还是较大影响

/**
 * @param
 * @Title: Dog
 * @Description:单例模式(不提供实例化构造方法,设为私有,提供获取唯一实例的方法)
 * 懒汉模式,类加载时,不实例化对象,等被调用获取对象方法时再实例化对象
 * 添加同步锁-线程安全
 * @return
 * @throws
 */
public class Lazy_dog{

    private static Lazy_dog lazy_dog;

    private Lazy_dog(){}

    public  static Lazy_dog getLazy_dog(){
        String temp="test";
        synchronized(temp){
            if(lazy_dog==null){
                try {
                    Class c=Class.forName("com.cheng.designpattern.singleton_mode.Lazy_dog");
                    Object object=c.newInstance();
                    Thread.sleep(3000);
                    lazy_dog=(Lazy_dog)object;
                }catch (Exception e){
                    System.out.println("反射创建实例Lazy_dog失败");
                }
            }
        }
        return lazy_dog;
    }

    public void call(){
        System.out.println("Lazy_dog-反射创建实例成功!");
    }

}

输出:可以看到在多线程的情况下产生同一实例

⑸懒汉式[延迟/按需加载](线程安全),在创建实例的方法内特定的代码添加重入锁

优点:线程安全,性能较好,重入锁更灵活

缺点:对性能有影响

/**
 * @param
 * @Title: Dog
 * @Description:单例模式(不提供实例化构造方法,设为私有,提供获取唯一实例的方法)
 * 懒汉模式,类加载时,不实例化对象,等被调用获取对象方法时再实例化对象
 * 添加同步锁-线程安全
 * @return
 * @throws
 */
public class Lazy_dog{

    private static Lazy_dog lazy_dog;

    private static Lock lock=new ReentrantLock();

    private Lazy_dog(){}

    public static Lazy_dog getLazy_dog(){

        lock.lock();
        if(lazy_dog==null){
            try {
                Class c=Class.forName("com.cheng.designpattern.singleton_mode.Lazy_dog");
                Object object=c.newInstance();
                Thread.sleep(3000);
                lazy_dog=(Lazy_dog)object;
            }catch (Exception e){
                System.out.println("反射创建实例Lazy_dog失败");
            }
        }
        lock.unlock();
        return lazy_dog;
    }

    public void call(){
        System.out.println("Lazy_dog-反射创建实例成功!");
    }

}

输出:可以看到在多线程的情况下产生同一实例

以上2-5都是懒汉式实现的单例模式,各有优缺点,主要演示线程安全的几种方式。

1.第2个例子为了首次加载类时,不实例化对象,在首次调用时,才实例化对象(因为首次调用时,有可能发生多线程同时调用,所以并发访问时,存在线程安全问题)。

2.第3-5个例子为了首次加载类时,不实例化对象,在首次调用时,才实例化对象(因为首次调用时,有可能发生多线程同时调用,因此使用线程锁的方式进行限制,所以并发访问时,也不存在线程安全问题)。

3.为了实现类的对象实例在内存当中唯一,不提供使用new方式进行实例化,故把构造函数设为私有化。

4.但得必须提供一个外部可以获取该实例的方法,所以必须实现一个对外开放的获取实例的方法。
 

至此,单例模式的demo演示完了,如有错漏,请告知。

猜你喜欢

转载自blog.csdn.net/Ouyzc/article/details/83540845