单例模式
饿汉式
线程安全
//饿汉式是线程安全的,是跟类一起实例化的,但是会浪费内存空间
public class HungryF {
private HungryF(){}
private static final HungryF hungryF = new HungryF();//跟类一起实例化
public static HungryF getInstance() {
return hungryF;
}
}
懒汉式
线程不安全
//懒汉式是线程不安全的 当执行new的时候cpu的执行步骤:先分配内存空间,初始化对象,让lazyF指向刚刚分配的空间,但是这个顺序可能会打乱
public class LazyF {
private LazyF() {}//构造器私有化
private static LazyF lazyF = null;//先不创建对象
public static LazyF getInstance() {
if (lazyF == null) {
lazyF = new LazyF();//单例实例化时创建对象
}
return lazyF;
}
}
通过synchronized修饰,安全,但不推荐
//此时饿汉式的线程安全的,但不推荐此方法
public class LazyS {
private LazyS(){}
private static LazyS lazyS = null;
public synchronized static LazyS getInstance(){
if (lazyS == null) {
lazyS = new LazyS();
}
return lazyS;
}
}
此双重检测锁不安全,因为没有volatile修饰
//双重锁同步锁是线程不安全的
//当执行new的时候cpu的执行步骤:先分配内存空间,初始化对象,让lazyF指向刚刚分配的空间,但是这个顺序可能会打乱
/*
cpu打乱的顺序为先分配内存空间、让lazyF指向刚刚分配的空间、初始化对象
* 此时有两个线程A、B
* 当A执行到第20行,B到第8行【执行到步骤二,即获得lazyT对象】
*A发现对象存在,直接执行第28行,但是此时还没有对象初始化,因此会有异常
* */
public class LazyT {
private LazyT(){}
private static LazyT lazyT = null;
private static LazyT getInstance() {
if (lazyT == null) {//第8行
synchronized (LazyT.class) {
if (lazyT == null) {
lazyT = new LazyT();//第20行
}
}
}
return lazyT;//第28行
}
}
枚举类是线程安全的
//因为枚举类是线程安全的,所以此方法创建也是安全的
public class LazyFi {
private LazyFi() {}
//在此枚举类中进行创建
private enum Singleton {
INSTANCE;//static final
private LazyFi lazyFi;
void Singleton() {
lazyFi = new LazyFi();
}
public LazyFi getInstance() {
return lazyFi ;
}
}
public static LazyFi getInstance() {
return Singleton.INSTANCE.getInstance();
}
}
证明枚举确实不能通过反射获取
public enum LazyFi2 {
INSTANCE;
public LazyFi2 getInstance() {
return INSTANCE;
}
}
class test {
public static void main(String[] args) throws Exception {
LazyFi2 instance = LazyFi2.INSTANCE;
Constructor<LazyFi2> constructor = LazyFi2.class.getDeclaredConstructor(String.class, int.class);//通过专业反编译软件可以看出枚举没有空参构造器
constructor.setAccessible(true);
LazyFi2 instance1 = constructor.newInstance(LazyFi2.class);
System.out.println(instance);//Cannot reflectively create enum objects这个异常证明枚举确实不能通过反射获取
System.out.println(instance1);
}
}
静态代码块实现,线程安全
//通过静态代码块实现,是线程安全的
public class LazyFo {
private LazyFo(){}
private static LazyFo lazyFo = null;
static {
lazyFo = new LazyFo();
}
public static LazyFo getInstance() {
return lazyFo;
}
}
volatile+双重检测锁是线程安全的
//线程安全,双重锁同步锁单例模式
public class LazySix {
private LazySix (){}
//volatile + 双重锁检验机制来禁止指令重排
private static volatile LazySix lazySix = null;
public static LazySix getInstance() {
if (lazySix == null) {
synchronized (LazySix.class) {
if (lazySix == null) {
lazySix = new LazySix();
}
}
}
return lazySix;
}
}
静态内部类
//懒汉式 静态内部类
public class LazySeven {
private LazySeven(){}
public static LazySeven getInstance() {
return InnerLazy.lazySeven;
}
public static class InnerLazy{
private static final LazySeven lazySeven = new LazySeven();
}
}
可以通过反射破解单例
//线程安全,双重锁同步锁单例模式
public class LazySix {
//设置标志,即通过红绿灯方法来防止破坏,看5又如何破坏
private static boolean flag = false;
private LazySix (){
synchronized (LazySix.class) {
if (flag == false) {
flag = true;
}else {
throw new RuntimeException("不要通过反射破坏");
}
}
//2.防止1的破坏
// synchronized (LazySix.class){
// if (lazySix != null) {//第25行
// throw new RuntimeException("不要通过反射破坏");
// }
// }
}
//volatile + 双重锁检验机制来禁止指令重排,此时是线程安全的
private static volatile LazySix lazySix = null;
public static LazySix getInstance() {
if (lazySix == null) {
synchronized (LazySix.class) {
if (lazySix == null) {
lazySix = new LazySix();
}
}
}
return lazySix;
}
public static void main(String[] args) throws Exception {
// LazySix instance = LazySix.getInstance();//此操作会返回一个对象,在第25行会检测出抛出异常,但如果不用此方法,都通过构造器创建对象,看第三步操作
Constructor<LazySix> constructor = LazySix.class.getDeclaredConstructor( null);//通过反射获取构造器
constructor.setAccessible(true);//无视私有构造器
//获取标志位,破坏其私有权限【5的前提步骤】
Field flag = LazySix.class.getDeclaredField("flag");
flag.setAccessible(true);
LazySix instance1 = constructor.newInstance();//通过构造器获取对象
flag.set(instance1, false);//5.又将其该位false,又破坏单例
LazySix instance2 = constructor.newInstance();//通过构造器获取对象
// System.out.println(instance.hashCode());//1.可以获取两个不同对象,破坏单例原则
System.out.println(instance1.hashCode());//3.两个对象都是构造器创建,又破坏了单例,看4来防止破坏,红绿灯方法
System.out.println(instance2.hashCode());
}
}