目录
单例和多例的区别和联系
- 单例模式和多例模式属于对象模式。
- 单例模式的对象在整个系统中只有一份,多例模式可以有多个实例。
- 它们都不对外提供构造方法,即构造方法都为私有。
单例模式的关键点:
- 构造方法为私有,这样外界就不能随意调用;
- get的方法为静态,由类直接调用。
多例模式(Multiton)
- 多例类可以有多个实例 ;
- 多例类必须能够自我创建并管理自己的实例,并向外界提供自己的实例。
详细说明
1. 什么是单例多例:
单例就是所有的请求都用一个对象来处理,比如我们常用的service和dao层的对象通常都是单例的,而多例则指每个请求用一个新的对象来处理,比如action;
2. 如何产生单例多例:
在通用的SSH中,单例在spring中是默认的,如果要产生多例,则在配置文件的bean中添加scope=”prototype”;
3. 使用单例多例的原因:
用单例,是因为没必要每个请求都新建一个对象,这样子既浪费CPU又浪费内存;
用多例,是为了防止并发问题;即一个请求改变了对象的状态,此时对象又处理另一个请求,而之前请求对对象状态的改变导致了对象对另一个请求做了错误的处理;
4. 用单例和多例的标准:
标准只有一个:当对象含有可改变的状态时(更精确的说就是在实际应用中该状态会改变),则多例,否则单例;
单例的两种模式
1、饿汉模式
在自己被加载时就将自己实例化,一般使用静态初始化的方法,称为饿汉式单例类。
2、懒汉模式
在第一次被引用时,才会将自己实例化,称为懒汉式单例类。
单例实现方法
1、静态常量(饿汉式)
public class Singleton {
//声明为final的变量,必须在类加载完成时已经赋值
private final static Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
}
优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
2、静态代码块(饿汉式)
public class Singleton {
private static Singleton INSTANCE;
//静态代码块,只在类加载时执行一次(保存在方法区)
static {
INSTANCE = new Singleton();
}
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
3、双重检查(懒汉式)
public class Singleton{
//volatile 保证了不同线程对这个变量进行操作时的可见性,
//即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的
private static volatile Singleton INSTANCE;
private Singleton(){}
public static Singleton getInstance(){
if(INSTANCE == null){
synchronized(Singleton.class){
if(INSTANCE == null){
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
双重检查(Double-Chechk Locking)可以保证多线程安全的原因是:当多个线程判断INSTANCE为null时,因为加了同步关键字,所以一个进程先进入完成后另一个进程再进入。如果没有第二个判断INSTANCE是否为null,则第二个进入的进程还会实例化一个对象。
优点:线程安全;延迟加载;效率较高。
4、静态内部类(懒汉式)
public class Singleton{
private Singleton(){};
//静态内部类
private static class SingletonInstance(){
//产生一个类的实例
private static final Singleton INSTANCE = new Singleton();
}
//只有在使用的时候才会调用静态内部类产生实例
public static Singleton(){
return SingletonInstance.INSTANCE;
}
}
种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
优点:避免了线程不安全,延迟加载,效率高。
多例实现方法
import java.util.*;
public class Multiton {
//允许产生的实例个数
private static int maxNumOfInstance = 3;
//识别是哪个实例的信息
private String instanceInfo;
private static ArrayList instanceList = new ArrayList(maxNumOfInstance); // 装实例的列表
//产生多个实例
static{
for(int i=0;i<maxNumOfInstance;i++){
instanceList.add(new Multiton("第"+i+"实例"));
}
}
private Multiton(){}
private Multiton(String info){
instanceInfo = info;
}
//选择策略(随机获取实例)
public static Multiton getIntance(){
Random random = new Random();
int instanceIndex = random.nextInt(maxNumOfInstance);
return (Multiton) instanceList.get(instanceIndex);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Multiton m1 = Multiton.getIntance();
System.out.println(m1.instanceInfo);
}
}
使用一个ArrayList保存多个实例,当使用时可以使用某种策略从其中选取一个实例。多例模式同样不向外部提供初始化方法,类实例的生成和分配有类自己决定和处理。