你用杯子喝可乐,喝完了不刷,继续去倒果汁喝,就是单例。
你用杯子喝可乐,直接扔了杯子,换个杯子去倒果汁喝,就是多例。html
数据库链接池就是单例模式,有且仅有一个链接池管理者,管理多个链接池对象。java
1. 什么是单例多例:
所谓单例就是全部的请求都用一个对象来处理,好比咱们经常使用的service和dao层的对象一般都是单例的,而多例则指每一个请求用一个新的对象来处理,好比action; spring
2. 如何产生单例多例:
在通用的SSH中,单例在spring中是默认的,若是要产生多例,则在配置文件的bean中添加scope="prototype"; 数据库
3. 为何用单例多例:
之因此用单例,是由于不必每一个请求都新建一个对象,这样子既浪费CPU又浪费内存;
之因此用多例,是为了防止并发问题;即一个请求改变了对象的状态,此时对象又处理另外一个请求,而以前请求对对象状态的改变致使了对象对另外一个请求作了错误的处理;
用单例和多例的标准只有一个:
当对象含有可改变的状态时(更精确的说就是在实际应用中该状态会改变),则多例,不然单例;设计模式
单例其实就在内存中该对象只有一个内存对应地址。不管你多少个线程访问那个对象,都是同一个地址。这样节省内存缓存
4. 什么时候用单例?什么时候用多例?
对于struts2来讲,action必须用多例,由于action自己含有请求参数的值,便可改变的状态;
而对于struts1来讲,action则可用单例,由于请求参数的值是放在actionForm中,而非action中的;
另外要说一下,并非说service或dao必定是单例,标准同第3点所讲的,就曾见过有的service中也包含了可改变的状态,同时执行方法也依赖该状态,但同样用的单例,这样就会出现隐藏的BUG,而并发的BUG一般很难重现和查找;安全
懒汉单例多线程
饿汉式并发
登记式单例性能
java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。
单例模式有如下特色:
一、单例类只能有一个实例。
二、单例类必须本身建立本身的惟一实例。
三、单例类必须给全部其余对象提供这一实例。
单例模式确保某个类只有一个实例,并且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具备资源管理器的功能。每台计算机能够有若干个打印机,但只能有一个Printer Spooler,以免两个打印做业同时输出到打印机中。每台计算机能够有若干通讯端口,系统应当集中管理这些通讯端口,以免一个通讯端口同时被两个请求同时调用。总之,选择单例模式就是为了不不一致状态,避免政出多头。
饿汉式和懒汉式区别
从名字上来讲,饿汉和懒汉,
饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,
而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。
另外从如下两点再区分如下这两种方式:
一、线程安全:
饿汉式天生就是线程安全的,能够直接用于多线程而不会出现问题,
懒汉式自己是非线程安全的,为了实现线程安全有几种写法,分别是上面的一、二、3,这三种实如今资源加载和性能方面有些区别。
二、资源加载和性能:
饿汉式在类建立的同时就实例化一个静态对象出来,无论以后会不会使用这个单例,都会占据必定的内存,可是相应的,在第一次调用时速度也会更快,由于其资源已经初始化完成,
而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要作初始化,若是要作的工做比较多,性能上会有些延迟,以后就和饿汉式同样了。
至于一、二、3这三种实现又有些区别,
第1种,在方法调用上加了同步,虽然线程安全了,可是每次都要同步,会影响性能,毕竟99%的状况下是不须要同步的,
第2种,在getInstance中作了两次null检查,确保了只有第一次调用单例的时候才会作同步,这样也是线程安全的,同时避免了每次都同步的性能损耗
第3种,利用了classloader的机制来保证初始化instance时只有一个线程,因此也是线程安全的,同时没有性能损耗,因此通常我倾向于使用这一种。
什么是线程安全?
若是你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。若是每次运行结果和单线程运行的结果是同样的,并且其余的变量的值也和预期的是同样的,就是线程安全的。
或者说:一个类或者程序所提供的接口对于线程来讲是原子操做,或者多个线程之间的切换不会致使该接口的执行结果存在二义性,也就是说咱们不用考虑同步的问题,那就是线程安全的。
应用
如下是一个单例类使用的例子,以懒汉式为例,这里为了保证线程安全,使用了双重检查锁定的方式:
public class TestSingleton { String name = null; private TestSingleton() { } private static volatile TestSingleton instance = null; public static TestSingleton getInstance() { if (instance == null) { synchronized (TestSingleton.class) { if (singleton == null) { singleton = new TestSingleton(); } } } return instance; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void printInfo() { System.out.println("the name is " + name); } }
能够看到里面加了volatile关键字来声明单例对象,既然synchronized已经起到了多线程下原子性、有序性、可见性的做用,为何还要加volatile呢,缘由已经在下面评论中提到,
还有疑问可参考http://www.iteye.com/topic/652440
和http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
public class TMain { public static void main(String[] args){ TestStream ts1 = TestSingleton.getInstance(); ts1.setName("jason"); TestStream ts2 = TestSingleton.getInstance(); ts2.setName("0539"); ts1.printInfo(); ts2.printInfo(); if(ts1 == ts2){ System.out.println("建立的是同一个实例"); }else{ System.out.println("建立的不是同一个实例"); } } }
运行结果:
结论:由结果能够得知单例模式为一个面向对象的应用程序提供了对象唯一的访问点,无论它实现何种功能,整个应用程序都会同享一个实例对象。
对于单例模式的几种实现方式,知道饿汉式和懒汉式的区别,线程安全,资源加载的时机,还有懒汉式为了实现线程安全的3种方式的细微差异。