1、�什么是单例?
单例顾名思义就是单个实例。日常工作都是自由的通过构造方法创建对象的,所以听到单例时,我们应该意识到其下是暗含了2层意思的,①构造方法不在为你所用,你不需要知道如何创建对象。②我会给你提供获取实例的方法,不需要你自己动手创建。
不清楚uml类关系的请移步UML--类图详解
2、�什么情况下使用单例?
当你需要内存中只有一个对象的时候。
3、为什么使用单例?
单例保证了内存中数据的唯一性,同时也降低了内存的开销。
3、�如何实现单例?
在1中了解到两层含义后,对我们创建单例类是有帮助的,总结下就是:①私有化构造方法 ②对外提供获取实例方法
- 饿汉式
所谓饿汉式,就是在类加载的时候就迫不及待的创建对象,等到调用获取实例方法直接拿到对象返回即可。
看实现代码:
public class HungarySingleton {
private static HungarySingleton intance = new HungarySingleton();
private HungarySingleton(){
}
public static HungarySingleton getInstance(){
return intance;
}
}
懒汉式是最简单的单例模式,在类加载的时候就实例化,避免了多线程问题的同时保证了实例唯一性。缺点是即使不需要时候也会被实例,会占用内存。所以推荐在实例占用内存不多的情况下使用,否则请使用下面几种方式。
- 懒汉式
懒汉式,就是在首次调用的时候才会创建实例,相比饿汉式,延迟了初始化。
public class LayzySingleton {
private static LayzySingleton instance = null;
private LayzySingleton() {
}
public LayzySingleton getInstatnce() {
if (null == instance) {
instance = new LayzySingleton();
}
return instance;
}
}
上面代码有个问题,就是在多线程调用中会有产生多个实例的隐患,所以需要使用线程同步:
public class LayzySingleton {
private static LayzySingleton instance = null;
private LayzySingleton() {
}
public synchronized LayzySingleton getInstatnce() {
if (null == instance) {
instance = new LayzySingleton();
}
return instance;
}
}
synchronized的加入虽然解决了多实例的隐患,但是又带来了性能低下的问题,因为我们只需要在首次创建对象时让同步产生作用即可,其后的调用无需同步,现在只要是调用该方法就同步,看来还需要改进
下面使用双重校验加锁(DCL):
public class LayzySingleton {
private static LayzySingleton instance = null;
private LayzySingleton() {
}
public LayzySingleton getInstatnce() {
if (null == instance) {
synchronized(LayzySingleton.class){
if (null == instance) {
instance = new LayzySingleton();
}
}
}
return instance;
}
}
现在多线程问题解决了,性能也得到优化了是不是双重校验加锁就完美了呢?
NO~
在Java并发编程中的指令重排序中有讲到双重校验加锁失效问题。
要禁止指令重排序需要使用volatile修饰变量
private volatile static LayzySingleton instance = null;
- 静态内部类
如果觉得饿汉式占内存,懒汉式又要考虑多线程问题,那么可以使用静态内部类可能是你想要的:
public class Singleton {
private Singleton(){
}
public Singleton getInstance(){
return Holder.instance;
}
private class Holder{
private static final Singleton instance = new Singleton();
}
}
静态内部类避免了加载Singleton类时就初始化问题,只有在调用getInstance时才会致使Holder类被加载并初始化,同时也避免了线程安全问题和性能问题,推荐使用此方法。
- 枚举式
如果觉得上面的还是麻烦,可以使用枚举单例,
代码实现:
public enum EnumSingleton {
INSTANCE ;
int num = 33;
String config = "jenson";
public void doSomething(){
}
}
枚举单例也可以有属性可以有方法,重要的是枚举默认就是线程安全的。