单例模式
单例模式属于创建型模式:注重完成对象的实例化(框架应用:struts 中的action)
单例模式:确保某个类在系统中仅有一个实例,并提供一个访问它的全局访问点
jvm 虚拟机中只有一个对象;
全局访问点:第一次被访问时完成了对象的实例化;
创建策略:构造函数私有化(其他类不能访问)-----限制单例类实例化
定义时含该类的静态私有对象-----创建单例类实例
单例模式写法:
该类只有一个实例为了实现该功能,必须隐藏类的构造器,即将构造器声明为 private 类型;并提供给一个创建对象的方法getInstance(),由于构造器私有化,外键无法直接创建该类型的实例对象,只能通过该类提供的方法来获取找个对象,要达到这一目的的也必须将创建对象的方法声明为static(要访问类静态的成员变量instance)。。。
public class Singleton {
//持有私有静态实例,不被引用,null值实现延迟加载
private static Singleton instance = null;
//私有构造方法,防止被实例化 (其他类无法直接访问)
private Singleton() {
}
//创建实例 第一次调用该方法时才会哇简称实例化
public static Singleton getInstance() {
if (instance == null) {
//实例化
instance = new Singleton();
}
//返回引用
return instance;
}
以上方法线程不安全,多线程情况处理:
1.进程锁:
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
synchronized :锁定对象,每次访问该方法都需要获取进程锁--成本太高,只需要在第一次实例化加锁就可以了。。
2.关键代码同步:
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
这种代码同步的方法避免了对方法同步,只有在实例化操作时需要加锁,但是线程不安全(如:线程A进入同步代码块,但在实例化单例类前线程被抢占;随后,线程B也进入if 语句,虽然B需要等A完成之前运算,但依然会创建两个不同的单一实例),在Java中创建对象和赋值时分两步(instance = new Singleton()语句)完成的(顺利不定)若只是对A 赋值,B线程也就不会在创建实例,但是返回实例时就会出错。。
3.双重同步锁:
public synchronized static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
解决被动初始化的线程安全&&两个线程同时访问getInstance()方法是怎么办?线程A 退出后 线程B会二次检查 看看单例类的实例是否为空,A会设置成员变量值,所有B不会创建第二个单例类的实例了;
4.单例模式用内部类维护单例的实现:
//内部类
private static class SingletonFactory{
private static Singleton instance = new Singleton();
}
//获取实例
public static Singleton getInstance(){
return SingletonFactory.instance;
}
JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的(第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且保存);
5.可以将创建实例与getInstance() 分开:
private static synchronized void syncInit() {
if (instance == null) {
instance = new SingletonTest();
}
}
public static SingletonTest getInstance() {
if (instance == null) {
syncInit();
}
return instance;
}
写一个方法(加同步),在getInstance() 方法中调用该同步方法即可;
单例模式中的实例化:
被动实例化(懒汉式):调用getInstance() 时实例化 上面几种都是--性能问题;
主动实例化(饿汉式):加载类时实例化单例类,不能延迟加载;
注意:
1.在大型程序中怎么找到单例类呢?----在程序的初始化段创建单例类。。
2.使用单例类,会有其他后果?----------创建单例类子类很难,除非但离妃尚未被实例化。。
3.网上书店的单例类设计在哪?----------目录类,所有用户共享一份书店目录。。
4.使用类的静态方法能实现单例效果吗?--单例类相比静态类更灵活。。
---静态类不能实现接口;
---单例类可以延迟加载,静态类一般在第一次加载时就初始化了;
---单例类可以被继承,方法可以被重写,但静态类的方法都是static的 不能被重写;
单例模式:5种写法(懒汉、饿汉、双重检验锁、静态内部类、枚举)。。。