最近快过年了,也没心思弄其他的,就把一些乱七八糟的总结总结写下吧,今天讲讲单例模式的几种写法,例如懒汉式、饿汉式,以及相关变种,有问题的请及时沟通。
何为单例模式?所谓单例模式,简单来说就是实例只能有一份的类。因此单例的核心就是构造器私有,这样外部类才不能通过new来创建这个类,只能调用这个类给外部的方法来创建一份实例。
1.饿汉式
public class SingleTon {
private SingleTon(){
}
private static final SingleTon SINGLETON = new SingleTon();
public static SingleTon getInstance(){
return SINGLETON;
}
}
优点:类装载的时候就实例化该类,避免了线程同步的问题
缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
2.饿汉式变种
public class SingleTon {
private static SingleTon singleTon;
static {
singleTon = new SingleTon();
}
private SingleTon(){
}
public static SingleTon getInstance(){
return singleTon;
}
}
这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
3.懒汉式
public class SingleTon {
private static SingleTon singleTon;
private SingleTon(){
}
public static SingleTon getInstance(){
if(singleTon == null){
singleTon = new SingleTon();
}
return singleTon;
}
}
优点:达到Lazy Loading的效果
缺点:只能在单线程中使用,多线程的情况下并发会产生多个实例
4.懒汉式变种(1)
public class SingleTon {
private static SingleTon singleTon;
private SingleTon(){
}
public static synchronized SingleTon getInstance(){
if(singleTon == null){
singleTon = new SingleTon();
}
return singleTon;
}
}
优点:达到Lazy Loading的效果,多线程情况下线程安全
缺点:把整个方法块都加锁了,所有进入这个方法的线程都得等待,效率低
5.懒汉式变种(2)
public class SingleTon {
private static SingleTon singleTon;
private SingleTon(){
}
public static SingleTon getInstance(){
if(singleTon == null){
synchronized (SingleTon.class){
singleTon = new SingleTon();
}
}
return singleTon;
}
}
优点:达到Lazy Loading的效果
缺点:线程不安全,虽然第4种将整个方法进行同步了,但是由于效率太低,就把初始化类给同步,但是还是会出现线程不安全的问题,当多个线程同时进入if(singleTon == null),都会继续往下走
6.懒汉式变种(3):双重检查
public 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;
}
}
优点:达到Lazy Loading的效果;线程安全;
注意:细心的同学能看到定义的singleTon被定义为volatile的,volatile简单来说就是提醒编译器实时注意定义变量的变化,不然双重检查时singleTon还是没变化,检查了就没用了。
7.懒汉式变种(4):静态内部类
public class SingleTon {
private SingleTon(){
}
private static class innerSingle{
private static final SingleTon SINGLETON = new SingleTon();
}
public static SingleTon getInstance(){
return innerSingle.SINGLETON;
}
}
优点:避免了线程不安全,延迟加载,效率高。
这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
适用场景:
(1)需要频繁的进行创建和销毁的对象;
(2)创建对象时耗时过多或耗费资源过多,但又经常用到的对象;
(3)工具类对象;
(4)频繁访问数据库或文件的对象。
以下都是单例模式的经典使用场景:
(1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
(2)控制资源的情况下,方便资源之间的互相通信。如线程池、数据库连接池等。