一、饿汉式(线程安全)
1.1静态常量式
public class Ehanshi_1 {
private Ehanshi_1() {}
private static Ehanshi_1 instance = new Ehanshi_1();
public static Ehanshi_1 getIntance() {
return instance;
}
}
优缺点:
- 优点:这种写法比较简单,就是在类装载的时候完成实例化。避免了线程同步问题。
- 缺点:在类转载的时候完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过该实例,在造成浪费。
1.2静态代码块
public class Ehanshi_1 {
private Ehanshi_1() {}
private final static Ehanshi_1 instance;
static {
instance = new Ehanshi_1();
}
public static Ehanshi_1 getIntance() {
return instance;
}
}
优缺点:
- 同上,同样会造成内存浪费。
二、懒汉式(线程不安全)
2.1正常无锁
public class Lazy_1 {
private Lazy_1() {}
private static Lazy_1 instance;
public static Lazy_1 getInstance() {
if(instance == null) {
instance = new Lazy_1();
}
return instance;
}
}
优缺点:
- 起到了Lazy loading的效果,但是只能在单线程下使用
- 如果在多线程情况下,一个线程进入判断语句,还未来的及实例化,另一个线程也通过了该判断语句,这时就会产生多个实例,所以此方法不能再多线程情况下使用。
2.2在方法上加锁
public class Lazy_1 {
private Lazy_1() {}
private static Lazy_1 instance;
public static synchronized Lazy_1 getInstance() {
if(instance == null) {
instance = new Lazy_1();
}
return instance;
}
}
优缺点:
- 解决了安全问题。
- 效率太低,每个线程来查询是否有对象的时候都需要上锁。而其实锁的只是第一次实例化即可,后面的线程来了直接返回即可。
2.3在方法中加锁
public class Lazy_1 {
private Lazy_1() {}
private static Lazy_1 instance;
public static Lazy_1 getInstance() {
if(instance == null) {
synchronized (Lazy_1.class) {
instance = new Lazy_1();
}
}
return instance;
}
}
优缺点:
- 这种方法虽然改进了2.2效率低下的问题,但是却出现了2.1的问题,同样可能会产生多个实例。
2.4双重检查
public class Lazy_1 {
private Lazy_1() {}
private static volatile Lazy_1 instance;
public static Lazy_1 getInstance() {
if(instance == null) {
synchronized (Lazy_1.class) {
if(instance == null) {
instance = new Lazy_1();
}
}
}
return instance;
}
}
优缺点:
- 加入双重检查,解决了线程安全,延迟加载,效率较高。
三、静态内部类
静态内部类不是与外部类一起加载的,而是延时加载,只有被调用的时候才加载,因此很适合单例模式。
public class StaticClass {
private StaticClass() {}
private static class Singleton{
private static final StaticClass instance = new StaticClass();
}
public static StaticClass getInstance() {
return Singleton.instance;
}
}
优缺点:
- 采用类加载的机制来保证初始化实例时只有一个线程
- 避免了线程安全问题,利用静态内部类特点实现延时加载,效率高。
四、枚举
enum Singleton{
INSTANCE;
public void sayOK(){
System.out.println("OK");
}
}
优缺点:
- 借助了jdk1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化攻击
五、单例模式的注意事项和细节说明
- 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
- 当想实例化一个单例类的时候,必须要记住使用相应获取对象的方法,而不是使用new
- 单例模式使用场景:需要频繁创建和销毁的对象、创建对象消耗时过多或耗费资源过多,当又经常用到的对象、工具类对象、频繁访问数据库或文件对象(比如数据源、session工厂等)