单例和多例的区别
原⽹址:
单例多例需要搞明⽩两个问题:
1. 什么是单例多例;
2. 如何产⽣单例多例;
3. 为什么要⽤单例多例
4. 什么时候⽤单例,什么时候⽤多例;
1. 什么是单例、多例:
所谓单例就是所有的请求都⽤⼀个对象来处理,⽐如我们常⽤的service和dao层的对象通常都是单例的,⽽多例则指每个请求⽤⼀个新的对象来处理,⽐如action;
⼀、单例模式和多例模式说明:
1.单例模式和多例模式属于对象模式。
2.单例模式的对象在整个系统中只有⼀份,多例模式可以有多个实例。
3.它们都不对外提供构造⽅法,即构造⽅法都为私有。
⼆、应⽤举例
1.单例模式举例:
第⼀种:懒汉式(线程不安全,加上synchronized后线程安全)
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized  Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
第⼆种:饿汉式(线程安全)
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
这种⽅式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中⼤多数都是调⽤getInstance⽅法, 但是也不能确定有其他的⽅式(或者其他的静态⽅法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。
public class Singleton {
private Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return this.instance;
}
}
表⾯上看起来差别挺⼤,其实差不多,都是在类初始化即实例化instance。
第三种:静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种⽅式同样利⽤了classloder的机制来保证初始化instance时只有⼀个线程,它跟饿汉式不同的是(
很细微的差别):饿汉式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),⽽这种⽅式是Singleton类被装载了,instance不⼀定被初始化。因为SingletonHolder类没有被主动使⽤,只有显⽰通过调⽤getInstance⽅法时,才会显⽰装载SingletonHolder类,从⽽实例化instance。想象⼀下,如果实例化instance很消耗资源,我想让他延迟加载,另外⼀⽅⾯,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地⽅被主动使⽤从⽽被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种⽅式相⽐饿汉式就显得很合理。
第四种:枚举
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
这种⽅式是Java作者提倡的⽅式,它不仅能避免多线程同步问题,⽽且还能防⽌反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个⼈认为由于1.5中才加⼊enum特性,⽤这种⽅式写不免让⼈感觉⽣疏,在实际⼯作中,我也很少看见有⼈这么写过。
public enum EnumTest {
MON, TUE, WED, THU, FRI, SAT, SUN;
}
public class Test{
public static void main(String[] args) {
for (EnumTest e : EnumTest.values()) {
System.out.String());
}
System.out.println("----------------我是分隔线------------------");
EnumTest test = EnumTest.MON;
switch (test) {
case MON:
System.out.println("今天是星期⼀");
break;
case TUE:
System.out.println("今天是星期⼆");
break;
// ... ...
default:
System.out.println(test);
break;
}
}
}
打印结果
MON
TUE
WED
THU
FRI
SAT
SUN
----------------我是分隔线------------------
今天是星期⼀
第五种:双重校验锁
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
单例模式的几种实现方式
这个是第⼀种⽅式的升级版,俗称双重检查锁定。在JDK1.5之后,双重检查锁定才能够正常达到单例效果。
2. 多例模式举例:
*;
import java.util.*;
class NumberFormatTest
{
public static void displayNumber(Double d,Locale l)
{
NumberFormat nf;
String dOut;
nf = NumberInstance(l);
dOut = nf.format(d);
System.out.println(dOut + " " + l.toString());
}
public static void main(String[] args)
{
displayNumber(1234567.89,new Locale("en","US"));
displayNumber(1234567.89,new Locale("de","DE"));
displayNumber(1234567.89,new Locale("fr","FR"));
displayNumber(1234567.89,new Locale("zh","CN"));
}
}
⼀个根据语⾔代码和地区代码格式化数字的多例模式例⼦
2. 如何产⽣单例、多例:
在通⽤的SSH中,单例在spring中是默认的,如果要产⽣多例,则在配置⽂件的bean中添加scope="prototype";
我就告诉你昨天我得问题你估计就明⽩了
我没⽤scope="prototype"就出现上⾯得验证问题了 连续点提交就这样
我添加后 不论怎么点都只会出现⼀个验证提⽰
3. 为什么⽤单例、多例:
之所以⽤单例,是因为没必要每个请求都新建⼀个对象,这样⼦既浪费CPU⼜浪费内存;
之所以⽤多例,是为了防⽌并发问题;即⼀个请求改变了对象的状态,此时对象⼜处理另⼀个请求,⽽之前请求对对象状态的改变导致了对象对另⼀个请求做了错误的处理;
⽤单例和多例的标准只有⼀个:
当对象含有可改变的状态时(更精确的说就是在实际应⽤中该状态会改变),则多例,否则单例;
4. 何时⽤单例?何时⽤多例?
对于struts2来说,action必须⽤多例,因为action本⾝含有请求参数的值,即可改变的状态;
⽽对于STRUTS1来说,action则可⽤单例,因为请求参数的值是放在actionForm中,⽽⾮action中的;
另外要说⼀下,并不是说service或dao⼀定是单例,标准同第3点所讲的,就曾见过有的service中也包含了可改变的状态,同时执⾏⽅法也依赖该状态,但⼀样⽤的单例,这样就会出现隐藏的BUG,⽽并发的BUG通常很难重现和查;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。