/**
* 单例模式的特点
*
* 1. 单例模式只能有一个实例。
*
* 2. 单例类必须创建自己的唯一实例。
*
* 3. 单例类必须向其他对象提供这一实例。
*
*
*/
/**单例类和静态类的对比
* 1. 单例可以继承和被继承,方法可以被override,而静态方法不可以。
*
* 2. 静态方法中产生的对象会在执行后被释放,进而被GC清理,不会一直存在于内存中。
*
* 3. 静态类会在第一次运行时初始化,单例模式可以有其他的选择,即可以延迟加载。
*
* 4. 基于2, 3条,由于单例对象往往存在于DAO层(例如sessionFactory),如果反复的初始化和释放,则会占用很多资源,而使用单例模式将其常驻于内存可以更加节约资源。
*
* 5. 静态方法有更高的访问效率。
*
* 6. 单例模式很容易被测试。
*
* 几个关于静态类的误解:
*
* 误解一:静态方法常驻内存而实例方法不是。
*
* 实际上,特殊编写的实例方法可以常驻内存,而静态方法需要不断初始化和释放。
*
* 误解二:静态方法在堆(heap)上,实例方法在栈(stack)上。
*
* 实际上,都是加载到特殊的不可写的代码内存区域中。
*
* 静态类和单例模式情景的选择:
*
* 情景一:不需要维持任何状态,仅仅用于全局访问,此时更适合使用静态类。
*
* 情景二:需要维持一些特定的状态,此时更适合使用单例模式。
*/
/**
* lazyloading也就是延迟加载的意思
* 延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。可以简单理解为,只有在使用的时候,才会发出sql语句进行查询。
* 延迟加载也称为惰性加载,即在长网页中延迟加载图像。用户滚动到它们之前,视口外的图像不会加载。
* 这与图像预加载相反,在长网页上使用延迟加载将使网页加载更快。
*在某些情况下,它还可以帮助减少服务器负载。
* 对于前端后台来说 lazyloading都有着应用
*
* 但是延迟加载对于后台开发来说,也有着巨大的弊端
* 第一,延迟加载搞不好就容易导致N+1 select问题,性能反而不能保障
* 第二,延迟加载一般是在ORM(对象关系映射)中采用字节码增强方式来实现,这种方式处理后的对象往往会导致对象序列化失效,而在大型web应用中一般都会采用 独立的缓存架构,一但应用系统引入独立的缓存系统对应用数据对象进行缓存,采用延迟加载后的对象序列化将失效,导致缓存失败。
* 第三,ORM中的延迟加载将业务对象的加载关系搞得不清不楚,如果某天想换ORM,那么还得针对不同的ORM处理延迟加载关系,即使不还ORM后来人想理解加载关系也会很头疼。
* 第四,延迟加载目的是一方面是对了使应用只加载必要的数据,减少数据传输量,提高查询速度。另一方面,为了减轻数据库的进行不必要查询而进行运行增加的压力,避免一次性进行过多的查询,减少系统消耗。对于第一个问题,通过必要的缓存一般可以解决,对于这点系统消耗一般还是可以承受;对于第二个问题,通过在业务层进行单表查询配合必要的索引一般也是不存在问题的。
* 第五,从另外一方面考虑,ORM需要承担的仅仅是O R M,和事务、缓存等特性一样,它们应该由其他更有资格的家伙来承担,不需要搞那么负载,否则对于以后的底层扩展,那可是一个艰巨的工作。
* 这段文字参考自 https://blog.csdn.net/sun11462/article/details/43369985
*/
public class Singleton {
//懒汉式
//可以lazyloading 但有一个巨大的问题就是线程不安全,如果多个线程同时调用,则会创建出许多对象
private static Singleton singleton;
private Singleton(){}
public static Singleton getSingleton(){
if(singleton==null){
return singleton = new Singleton();
}
return singleton;
}
//线程安全
// 然而并发其实是一种特殊情况,大多时候这个锁占用的额外资源都浪费了,这种打补丁方式写出来的结构效率很低。
public static synchronized Singleton getSingleton1(){
if(singleton==null){
return singleton = new Singleton();
}
return singleton;
}
}
//饿汉式,直接在本类创建了一个对象,用的时候直接loading
class SingletonDemo{
private static SingletonDemo singletonDemo = new SingletonDemo();
private SingletonDemo(){}
public static SingletonDemo getSingletonDemo(){
return singletonDemo;
}
}
//静态类内部加载
/**
* 使用内部类的好处是,静态内部类不会在单例加载时就加载,而是在调用getInstance()方法时才进行加载,
* 达到了类似懒汉模式的效果,而这种方法又是线程安全的,不会说创建多个对象
*/
class StaticSingleton{
private static class StaticSingletonHandler{
private static StaticSingleton staticSingleton = new StaticSingleton();
}
private StaticSingleton(){ }
public static StaticSingleton getStaticSingleton(){
return StaticSingletonHandler.staticSingleton;
}
}
//优雅的写法 枚举
enum SingletonD{
Instance;
public void saying(){
System.out.println("枚举构建单例模式");
}
}
//双重校验锁法
/**
* 双锁机制 第一个锁 锁引用,第二个锁 锁实例化
* 1.线程A访问getInstance()方法,因为单例还没有实例化,所以进入了锁定块。
*
* 2.线程B访问getInstance()方法,因为单例还没有实例化,得以访问接下来代码块,而接下来代码块已经被线程1锁定。
*
* 3.线程A进入下一判断,因为单例还没有实例化,所以进行单例实例化,成功实例化后退出代码块,解除锁定。
*
* 4.线程B进入接下来代码块,锁定线程,进入下一判断,因为已经实例化,退出代码块,解除锁定。
*
* 5.线程A初始化并获取到了单例实例并返回,线程B获取了在线程A中初始化的单例。
*
* 双重校验锁法是线程安全的,并且,这种方法实现了lazyloading。
* 此段代码参考自https://www.cnblogs.com/cielosun/p/6582333.html
*/
class DoubleSingletonDemo {
private volatile static DoubleSingletonDemo instance;
private DoubleSingletonDemo(){
System.out.println("Singleton is loading");
}
public static DoubleSingletonDemo getInstance(){
if(instance==null){
synchronized (DoubleSingletonDemo.class){
if(instance==null){
instance=new DoubleSingletonDemo();
}
}
}
return instance;
}
}
class Test{
public static void main(String[] args) {
SingletonDemo singletonDemo = SingletonDemo.getSingletonDemo();
System.out.println(singletonDemo);
//枚举调用单例(优雅)
SingletonD.Instance.saying();
}
}