单例模式
一个类仅有一个实例,所有其他类对象共享该实例进行操作
特点:只能由自己的公共方法创建一个实例,只对外提供该唯一实例
应用场景:资源共享文件与控制资源的操作(如日志文件,应用配置,数据库连接池,线程池)
应用实例:windows task manager,数据库连接池,OS的文件系统,回收站...
实现方法:饿汉,懒汉(饱汉),双检索,静态内部类,枚举类
- 懒汉模式只有需要单例时才去初始化实例,线程一般不安全
- 饿汉模式在类加载阶段就将实例初始化完成,这样保证线程安全
- 双检查模式,不是对这个方法进行synchronize,仅仅在实例化步骤前进行2次synchronize该类
- 静态内部类,保证了线程安全,在内部类中实例化唯一实例,读取实例没有同步保证性能
- 枚举模式,默认线程安全
相关问题:
- clone能否产生一个新的单例模式类的实例?
不能,单例类必须是final的不能被继承,同时假如单例类继承于有clone方法的类,需要重写clone,是它抛出异常- 在getInstance()方法上同步有优势还是仅同步必要的块更优优势?
由于锁有开销,因此仅同步必要的块(创建实例部分)就行了,其他时候仅仅是对实例的只读操作,保证性能- 双检索有什么缺点?
需要防止初始化实例时,指令重排序,因此最好对指定变量使用volatile修饰,防止指令重排序(jdk5.0以后引入)- 如何获取更多的单例对象,如何防止?
反射机制,防止的办法就是在构造函数中判断是第几次被调用,假如不是首次调用就立即抛出异常。还有一个办法就是使用枚举类模式实现单例
Constructor con = Singleton. class. getDeclaredConstruction( ) ;
con. setAccessible( true) ;
//通过反射获取实例
Singleton instance = ( Singleton) con. newInstance( ) ;
枚举类型
- 用来表示常量,作为switch的选择类型,继承自java.lang.Enum,因此不能继承其他类
- 在类中添加其他方法,方法必须在实例后面,最后一个实例后需要接";"
- 枚举集合:java.util.EnumSet,java.util.EnumMap
实现方法实践
//单例模式的实现方式
// 懒汉(饱汉),一般没有对方法同步,多线程可能存在生成多个实例,优点是效率高,但是不安全,
// 有时候直接类加载就初始化静态变量,浪费内存
class Singleton{
private static Singleton singleton;
private Singleton(){};
public static Singleton getInstance(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
//饿汉,类加载就产生实例
class Singleton{
private static Singleton singleton = new Singleton();
private Singleton(){};
public static synchronized Singleton getInstance(){
return singleton;
}
}
//双重检测,对实例化块进行2次检验,使用同步
class Singleton{
private static volatile Singleton singleton;
private Singleton(){};
public static Singleton getInstance(){
if(singleton == null){
synchronized(Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
//静态内部类,不用使用同步,线程安全.内部类私有性质保证只有getInstance能够调用它
class Singleton{
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
private Singleton(){};
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
//枚举类,默认线程安全,这种方式绝对防止反射调用私有构造器来破坏单例
public enum Singleton{
INSTANCE;
private String name;
public void dosomething(){
doing...
}
}
网友评论