<其它设计模式介绍及案例源码下载 >
简介:单例模式(Singleton Pattern)涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
主要解决:一个全局使用的类实例频繁地创建与销毁。
优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景: 1、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行 2、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
实现案例:单例模式根据需要目前比较流行的机种方式为饿汉式(Hungry)、懒汉式(LazyClass)、双检锁(DCLClass)
注意:懒汉式与饿汉式的根本区别在与是否在类内方法外创建自己的对象。并且声明对象都需要私有化,构造方法都要私有化,这样外部才不能通过 new 对象的方式来访问。饿汉式的话是声明并创建对象(因为他饿),懒汉式的话只是声明对象,在调用该类的 getinstance() 方法时才会进行 new 对象。
例如下:
/** * 是否 Lazy 初始化:是 * 是否多线程安全:是 * 实现难度:易 * 描述:这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。 * 优点:第一次调用才初始化,避免内存浪费。 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。 */ public class LazyClass { private static LazyClass lc; private LazyClass() { super(); // TODO Auto-generated constructor stub } public static synchronized LazyClass getInstance() { if(lc==null) { lc= new LazyClass(); } return lc; } } /** * 是否 Lazy 初始化:否 * 是否多线程安全:是 * 实现难度:易 * 描述:这种方式比较常用,但容易产生垃圾对象。 * 优点:没有加锁,执行效率会提高。 * 缺点:类加载时就初始化,浪费内存。 * */ public class HungryClass { private static HungryClass lc= new HungryClass(); private HungryClass() { super(); // TODO Auto-generated constructor stub } public static HungryClass getInstance() { return lc; } } /** * 是否 Lazy 初始化:是 * 是否多线程安全:是 * 实现难度:较复杂 * 描述:这种方式采用双锁机制,安全相比于懒汉式而言在多线程情况下仍能保持较高性能。 */ public class DCLClass { /*volatile关键字说明:java内存模型规定所有的变量都存放在主内存当 * 中,每个线程在执行的时候,会从主内存当中拷贝一份到自己的工作内存当 * 中,线程对变量的读取,操作都是在工作内存当中执行的,不同线程之间也 * 不能相互访问其他线程的工作内存,线程之间的变量传递需要通过主内 * 存来实现共享。那么什么时候把修改过得变量更新到主内存当中去,volatile * 关键字就是为了解决数据一致性的问题,通俗来说就是线程A对变量的修改,会直 * 接刷新到主内存,线程B当中,在对变量进行读取的时候,发现变量是volatile关键字 * 修饰的变量,直接放弃从工作内存当中读取,而是从主内存中读取 */ private static volatile DCLClass dcl; private DCLClass() { super(); // TODO Auto-generated constructor stub } public static DCLClass getInstance() { if (dcl==null) { synchronized(Class.class) { if (dcl==null) { dcl=new DCLClass(); } } } return dcl; } } public class TestClass { public static void main(String[] args) { Thread t1=new Thread(new Runnable() { @Override public void run() { int x=0; while(x<1000) { x++; System.out.println("线程一懒汉式:"+LazyClass.getInstance().hashCode()); System.out.println("线程二饿汉式:"+HungryClass.getInstance().hashCode()); System.out.println("线程二双检索:"+DCLClass.getInstance().hashCode()); } } }); Thread t2=new Thread(new Runnable() { @Override public void run() { int x=0; while(x<1000) { x++; System.out.println("线程二懒汉式:"+LazyClass.getInstance().hashCode()); System.out.println("线程二饿汉式:"+HungryClass.getInstance().hashCode()); System.out.println("线程二双检索:"+DCLClass.getInstance().hashCode()); } } }); t1.start(); t2.start(); } }
说明:在将输出结果进行排序后,同一种类所创建的对象对应的hashcode值是一致的(同一个对象)