[置顶] 单例设计模式

概念和图解

概念

单例设计模式定义:确保这个类只有一个实例,并且自动的实例化向系统提供这个实例。

图解

经典代码

package com.cloud.day1;

public class Singleton {

   //使用精态变量记录唯一实例

   private static Singleton singleton;

   //构造器定义为私有的只有内部才可以使用

   private Singleton(){}

   public static Singleton getInstance(){

      if(singleton == null){

        singleton = new Singleton();

      }

      return singleton;

   }

   public void otherMethon(){

      System.out.println("其他方法...");

   }

}


说明:

Singleton称为单例类,构造函数使用private修饰,确保系统中只能产生一个实例,并且自动生成的。上面代码也就是所谓的懒汉式加载:只有到使用该对象的时候才来创建,意思饿了才来做饭吃。


线程安全问题

在上面的代码中存在一个很明显的线程安全问题,当有多条线程来请求对象实例的时候,因为对象的创建是需要时间的,假设A线程进来判断singleton == null就会进入对象的创建过程,这时如果同时在过来几条线程,那么他们都会得到一个对象实例,这个就是所谓的线程安全问题。

同步方法

package com.cloud.day1;

public class Singleton {

   //使用精态变量记录唯一实例

   private static Singleton singleton;

   //构造器定义为私有的只有内部才可以使用

   private Singleton(){}

   //有线程要进来,必须等待其他线程离开才可以

   public static synchronized Singleton getInstance(){

      if(singleton == null){

        singleton = new Singleton();

      }

      return singleton;

   }

}

该方法会影响系统的性能

饿汉式加载

package com.cloud.day1;

public class Singleton {

   //使用精态变量记录唯一实例

   private static Singleton singleton = new Singleton();

   //构造器定义为私有的只有内部才可以使用

   private Singleton(){}

   public static Singleton getInstance(){

      return singleton;

   }

}

这里先把对象创建出来,有需要直接使用,也就是意思先把饭做好,饿了就直接吃,所以叫饿汉式加载。


双重检查

package com.cloud.day1;

public class Singleton {

   //使用精态变量记录唯一实例

   //volatile可以确保当singleton被初始化后,多线程可以正确处理

   private volatile static Singleton singleton;

   //构造器定义为私有的只有内部才可以使用

   private Singleton(){}

   public static Singleton getInstance(){

      //如果实例不存在,则进入同步区

      if(singleton == null){

        //只有第一次才会彻底执行这里面的代码

        synchronized (Singleton.class) {

           //再次检查,不存在就创建

           if(singleton == null){

              singleton = new Singleton();

           }

        }

      }

      return singleton;

   }

}

 

听说JVM垃圾回收机制会吃掉单例对象,这个是很早年代的bug了,现在已经没有了。

单例模式应用

真假美猴王

package com.cloud.exe;

import java.util.ArrayList;

import java.util.Random;

public class Monkey {

   //两个美猴王,一个真一个假

   private static int twoMonkey = 2;

   //定义一个容器存放两只猴子

   private static ArrayList<Monkey> monkeyList = new ArrayList<Monkey>();

   //定义容器存放两只美猴王的名字

   private static ArrayList<String> nameList = new ArrayList<String>();

   //定义两只美猴王

   static{

      for(int i=0;i<twoMonkey;i++){

        monkeyList.add(new Monkey("美猴王"+(i+1)+""));

      }

   }

   //私有构造方法

   private Monkey(){}

   private Monkey(String name){

      nameList.add(name);

   }

   //随机获取一只美猴王

   private static int numRandom = 0;

   public static Monkey getInstance(){

      Random random = new Random();

      numRandom = random.nextInt(twoMonkey);

      return monkeyList.get(numRandom);

   }

   //谁是真的美猴王

   public void say(){

      System.out.println(nameList.get(numRandom)+":我是真的美猴王!");

   }

   public static void main(String[] args) {

      int shenxian = 9;

      for(int i=0;i<shenxian;i++){

        Monkey monkey = Monkey.getInstance();

        System.out.print(""+(i+1)+"个神仙得到的回答:");

        monkey.say();

      }

   }

}

运行效果:

第1个神仙得到的回答:美猴王2号:我是真的美猴王!

第2个神仙得到的回答:美猴王1号:我是真的美猴王!

第3个神仙得到的回答:美猴王1号:我是真的美猴王!

第4个神仙得到的回答:美猴王2号:我是真的美猴王!

第5个神仙得到的回答:美猴王1号:我是真的美猴王!

第6个神仙得到的回答:美猴王2号:我是真的美猴王!

第7个神仙得到的回答:美猴王2号:我是真的美猴王!

第8个神仙得到的回答:美猴王2号:我是真的美猴王!

第9个神仙得到的回答:美猴王1号:我是真的美猴王!

到底谁是美猴王还是去问如来佛祖吧!!!

单例模式优缺点

优点:

1.      单例模式只会创建一个对象实例,减少内存消耗

2.      设置全局访问点,优化共享资源的访问

缺点:

1.      没有接口,很难扩展

2.      不利于测试

3.      与单一职责原则冲突

单例模式总结

spring中,每个Bean默认就是单例,这样Spring容器可以管理Bean的生命周期。这个有个注意点就是Java的JVM垃圾回收机制,当一个对象在内存中长期不使用的时候,就会被回收需要使用的时候再来创建,解决方案:

1、  容器存放单例对象,例如Spring中的Bean对象

2、  资源文件记录单例的状态,即使被销毁了重新创建数据不会丢失

你可能感兴趣的:(java,设计模式,Singleton,实例)