Mr.Chrosing`s Home

[Motto:最后的丰收对得起现在的汗水]

:D 获取中...


这以前是一个很懒的家伙,现在要变勤快啦.


Welecome to Chrosing`s Home

设计模式之单例模式(SingletonPattern)

1.单例模式

package cn.nasity.design.mode.SingletonPattern;

/**
 * @author : Chrosing
 * @version : 1.0
 * @date : 2020/2/17
 */
public class Singleton {

    /**
     * 单例模式 必须进行private
     */
    private Singleton(){}

    private static Singleton instance = new Singleton();

    public static Singleton getInstance(){
        return instance;
    }

}

创建SingletonPatternDemoMain.java

package cn.nasity.design.mode.SingletonPattern;

/**
 * @author : Chrosing
 * @version : 1.0
 * @date : 2020/2/17
 */
public class SingletonPatternDemoMain {
    public static void main(String[] args) {

        //不合法的构造函数
        //编译时错误:构造函数 SingleObject() 是不可见的
        //SingleObject object = new SingleObject();

        //获取唯一可用的对象
        SingleObject object = SingleObject.getInstance();

        //显示消息
        object.showMessage();
    }
}

1

可以输出单例模式中的Hello world!

2.单例模式中的 饿汉式 和 懒汉式

单例模式有两种方式 :饿汉式和懒汉式
符合以下条件线程安全
(1)、在多线程环境下
(2)、必须有共享资源
(3)、对资源进行非原子性操作

①.饿汉式

package cn.nasity.design.mode.SingletonPattern;

/**
 * @author : Chrosing
 * @version : 1.0
 * @date : 2020/2/17
 */
public class Singleton {

    /**
     * 单例模式 必须进行private
     */
    private Singleton(){}

    private static Singleton instance = new Singleton();

    public static Singleton getInstance(){
        return instance;
    }

}
package cn.nasity.design.mode.SingletonPattern;

/**
 * @author : Chrosing
 * @version : 1.0
 * @date : 2020/2/17
 */
public class Main {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();
        Singleton singleton3 = Singleton.getInstance();

        System.out.println(singleton1);
        System.out.println(singleton2);
        System.out.println(singleton3);
    }
}

2

结论:是线程安全的

②.懒汉式

package cn.nasity.design.mode.SingletonPattern;

/**
 * @author : Chrosing
 * @version : 1.0
 * @date : 2020/2/17
 */
public class Singleton2 {

    private Singleton2(){}

    private static Singleton2 instance = null;

    public static Singleton2 getInstance(){
        if(instance == null){
            try {
                Thread.sleep(1000);
            }catch (Exception e){
                e.printStackTrace();
            }
            instance = new Singleton2();
           
        }
        return instance;
    }
}
package cn.nasity.design.mode.SingletonPattern;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author : Chrosing
 * @version : 1.0
 * @date : 2020/2/17
 */
public class Main {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "  " + Singleton2.getInstance());
                }
            });
        }
    }
}

3

结论:很明显返回的不是同一个对象 ,所以懒汉式式线程不安全的
那么解决懒汉式的线程安全问题可以对实例化对象的静态方法加上synchronized关键字进行修饰

package cn.nasity.design.mode.SingletonPattern;

/**
 * @author : Chrosing
 * @version : 1.0
 * @date : 2020/2/17
 */
public class Singleton2 {

    private Singleton2(){}

    private static Singleton2 instance = null;

    // 1 未加任何机制锁
    // public static Singleton2 getInstance(){
    // 修改前  ↑  修改后 ↓
    // 2.加了锁
    public static synchronized Singleton2 getInstance(){
        if(instance == null){
            try {
                Thread.sleep(1000);
            }catch (Exception e){
                e.printStackTrace();
            }
            instance = new Singleton2();
        }
        return instance;
    }
}

4

并没有创建多个实例
以上方式虽然解决了线程安全问题,但是从性能角度考虑并不高明
所以我们可以把锁的范围缩小,双重检查加锁制

package cn.nasity.design.mode.SingletonPattern;

/**
 * @author : Chrosing
 * @version : 1.0
 * @date : 2020/2/17
 */
public class Singleton2 {

    private Singleton2(){}

    private static Singleton2 instance = null;

    public static Singleton2 getInstance(){
        if(instance == null){
            try {
                Thread.sleep(1000);
            }catch (Exception e){
                e.printStackTrace();
            }
            // 修改此处 增加锁
            // instance = new Singleton2();
            synchronized (Singleton2.class){
                if(instance==null){
                    instance = new Singleton2();
                }
            }
        }
        return instance;
    }
}

但是上面的代码还是有问题 ,因为jvm虚拟机对程序执行有优化 ,会出现指令重排序问题,所以会导致

if(instance==null){
      instance = new Singleton2();
 }

这个判断可能不为空的时候,那么又会出现问题
所以要解决根本问题需要对instance这个实例进行volatile修饰

package cn.nasity.design.mode.SingletonPattern;

/**
 * @author : Chrosing
 * @version : 1.0
 * @date : 2020/2/17
 */
public class Singleton2 {

    private Singleton2(){}

    // 增加volatile修饰
    private static volatile Singleton2 instance = null;

    // 1 未加任何机制锁
    // public static Singleton2 getInstance(){
    // 修改前  ↑  修改后 ↓
    // 2.加了锁
    public static Singleton2 getInstance(){
        if(instance == null){
            try {
                Thread.sleep(1000);
            }catch (Exception e){
                e.printStackTrace();
            }
            // 修改此处 增加锁
            // instance = new Singleton2();
            synchronized (Singleton2.class){
                if(instance==null){
                    instance = new Singleton2();
                }
            }
        }
        return instance;
    }
}

3.懒汉式还可以通过静态内部类与类加载器的机制来实现

package cn.nasity.design.mode.SingletonPattern;

/**
 * @author : Chrosing
 * @version : 1.0
 * @date : 2020/2/17
 */
public class Singleton3 {
    private Singleton3() {}

    private static class staticClassLazy {
        private static Singleton3 single = new Singleton3();
    }

    public static Singleton3 getInstance() {
        return staticClassLazy.single;
    }
}

6

这个性能相比较 性能优

4.体会

单例模式最重要的就是体现了他在模式中 只实例化一次或者不实例化 这样可以避免多次创建从而不影响消耗

5.介绍

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

应用实例

  • 1、一个班级只有一个班主任。

  • 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。

  • 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。
    优点:

  • 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。

  • 2、避免对资源的多重占用(比如写文件操作)。
    缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

使用场景

  • 1、要求生产唯一序列号。
  • 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  • 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
    注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。
最近的文章

设计模式之建造者模式(BuilderPattern)

1.前言解释我们假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Colddrink)。汉堡(Burger)可以是素食汉堡(VegBurger)或鸡肉汉堡(ChickenBurger),它们是包在纸盒中。冷饮(Colddrink)可以是可口可乐(coke)或百事…

继续阅读
更早的文章

设计模式之工厂模式(FactoryPattern)

1.创建工厂类接口形状Shape实现画的方法Shape.javapackagecn.nasity.design.mode.FactoryPattern;/***工厂模式创建一个接口供子类复现形状的工厂*@author:Chrosing*@version:1.0*@date:2020/2/15*/pu…

继续阅读