单例模式是为了让减少某个对象因为多次使用而反复创建,第一次调用singleton类时,会新建出singleton对象,但之后访问时,返回的是第一次新建的instance。
注意:
事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。
1.饿汉模式
public class SingleEasy {
private static SingleEasy singleEasy=new SingleEasy();
}
缺点:第一步加载class文件就已经实例化了,没有做到想用时才实例化
优点:类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
2.懒汉模式:
在第一次调用的时候实例化自己,每次调用getInstance时都需要进行同步开销
- 线程不安全
public class Singleton {
private Singleton() {//private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。
}
private static Singleton single = null;
//静态工厂方法
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
但是这种写法的懒汉单例模式没有考虑线程安全问题,它是线程不安全的,在并发环境下很可能出现多个Singleton实例。为了保证懒汉单例模式的线程安全,可以对getInstance这个方法改造:
- 线程安全
(1)在getInstance方法上加同步,会消耗一些性能
public class Singleton {
private Singleton() {
}
private static Singleton single = null;
public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
(2)双重检查锁定
public class Singleton {
private Singleton() {
}
private static Singleton single = null;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
(3)静态内部类
这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。
public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
3.DCL单例模式(双检查模式):
基本是线程安全的,
volatile:同步指令集,防止乱序,JDK5之后支持多并发制定集
public class DCLSingle {
private volatile static DCLSingle instance;
public static DCLSingle getInstance() {
if (instace == null) {
synchronized (DCLSingle.class) {
if (instance == null) {
instance = new DCLSingle();
}
}
}
return instace;
}
}
class文件中方法的字节码流就是由JVM的指令序列构成的
Jdk5 以后支持处理器乱序执行 汇编指令
导致 指向地址和实例化堆区 顺序不同
JVM调用方法的底层机制:
第一步:提取Client.class字节码文件放到方法区,在常量池有一个DCLSigle的符号引用(这个引用没有指向真正类信息的地址)
第二步:运行到讲DCLsingle.class字节码信息加载到内存,这时DCLSingle的符号变量指向Class类信息
第三步:声明DCLsigle类型,sigle变量,指向DCLSigle内存空间
第四部:在堆区开辟空间,实例化DCLSigle对象。
4.枚举:
(1)枚举中的属性必须放在最前面
(2)枚举中可以和java类一样定义方法
(3)枚举中的构造方法必须是私有的
通过一个java类来模拟枚举的功能:
public enum EnumManager {
SDCardManager(10)
{
@Override
public EnumManager getSingle() {
return SDCardManager;
}
}
,
HttpManager(1) {
@Override
public EnumManager getSingle() {
return null;
}
};
public SdCardImpl getSingleton()
{
return new SdCardImpl();
}
public abstract EnumManager getSingle();
private EnumManager(int type)
{
}
}