一、懒汉式
懒汉式是对象用不用都已经创建了,这样比较浪费内存。
public class Hungry {
//重要:构造器私有化
private Hungry(){
}
//创建静态对象,静态总在内存中加载一份
public static final Hungry hungry =new Hungry();
//获取对象方法
public static Hungry getInstance(){
return hungry;
}
}
二、懒汉式DCL
可以理解为我什么时间用什么时间创建对象,节省内存。
/**
* 懒汉式--单例模式
*/
public class Lazy {
private Boolean flag=false;
private Lazy(){
synchronized (Lazy.class){
if (flag==false){
flag=true;
}else {
throw new RuntimeException("反射破解异常报错");
}
}
}
public volatile static Lazy lazy;//用volatile修饰,禁止指令重排
/**
* 双重检测模式 DCL懒汉式
* @return
*/
public static Lazy getInstance(){
if (lazy==null){
//针对多线程加锁,总是一个对象
synchronized (Lazy.class){
//第一个线程判空成功之后,使用synchronized关键字对赋值操作进行加锁,第二次判空之后开始了创建对象并赋值的操作
if (lazy == null) { //对于单线程是安全的,一个对象
//原子性是指在一个操作中,cpu不可以在中途暂停然后再调度,要么一次执行完成,要么就不执行。
lazy = new Lazy(); //不是原子性操作
/**
* 注意,这里的操作并不是原子性的,简单来说,这里的操作可以分成三步:
* 1.创建内存空间
* 2.在内存空间内创建对象
* 3.将内存空间赋值给变量lazy
*/
/**
* 上面的操作由于不是原子性,所以三步之间是可以被中断的,再加上不是原子操作,
* 所以可能会进行重排序,所以又产生了有序性问题
* 如果将步骤2和步骤3进行了重排序,创建完内存之后立刻赋值,赋值之后再进行对象的创建,
* 而另外一个线程在赋值和对象的创建之间对变量singleton 进行了访问,那么他就会拿到一个半成品的对象。
* 这时就会出现空指针异常。这个问题是由顺序性和原子性两个原因共同导致的。
*
* 所以synchronized关键字对原子性的保证是从结果上保证的,因为对于整个赋值操作,无论是否重排序,
* 确实没有影响结果,但是对于另外一个线程来讲就不尽相同了。
*/
}
}
}
return lazy;
}
}
三、反射破坏单例模式(暴力反射)
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Lazy lazy1 = Lazy.getInstance();
System.out.println(lazy1);
// 采用反射,可以破坏懒汉式单例
Constructor<Lazy> constructor = Lazy.class.getDeclaredConstructor(null);
// 设置构造器可以外部访问
constructor.setAccessible(true);
Lazy lazy = constructor.newInstance();
System.out.println(lazy);
}
输出结果是两个不同的实例对象。
四、枚举实现单例模式
源码如下:
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
//不能通过反射创建枚举对象
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
利用枚举实现单例代码:
public enum SinglePattern {
INSTANCE;
private SinglePattern() {
}
public static SinglePattern GetInstance() {
return INSTANCE;
}
}