将类的构造方法设置为private,通过静态方法获取单例
/**
* 饿汉模式:它很饿,在类的初始化时就会创建单例,类初始化创建单例时会上jvm层面的一个锁,
* 所以不会有线程安全问题
*/
public class HungryDemo1 {
private HungryDemo1(){
//避免反射破坏单例
throw new RuntimeException("无权访问该构造");
}
private static final HungryDemo1 hungryDemo1=new HungryDemo1();
public static HungryDemo1 getInstance(){
return hungryDemo1;
}
}
/**
* 饱汉模式:它比较饱,只有需要单例时才会创建,如果多线程同时获取单例时会出现线程安全问题可能创建出多例,
* 可以给静态方法加synchronized关键字解决,但是这样即便单例已经创建过了,每次获取也还会上锁阻塞,
* 所以可以改进为双重检查锁的方式,但是创建对象的过程不是一个原子操作,
* new一个对象分为3步骤,开辟内存空间,给对象的属性赋值,将对象暴露为可用,
* 因为jvm对代码有一个优化机制,所以可能会造成指令重排序导致第三步比第二步先执行导致对象
* 属性未填充就提前暴露被其他线程获取这就线程不安全了,所以可以通过给单例属性加volatile关键字禁止指令重排序解决。
*/
public class LazyDemo1 {
private LazyDemo1(){
//避免反射破坏单例
throw new RuntimeException("无权访问该构造");
}
private static volatile LazyDemo1 lazyDemo1;
public static LazyDemo1 getInstance(){
if(lazyDemo1==null){
synchronized(LazyDemo1.class){
if(lazyDemo1==null){
lazyDemo1=new LazyDemo1();
}
}
}
return lazyDemo1;
}
}
静态内部类单例
静态内部类单例也是一种懒汉模式的单例(饱汉模式)
一个类只有被获取属性或者要被使用时才会加载,而且类初始化时会有jvm层面的锁所以不会出现线程安全问题。
public class LazyDemo2 {
private LazyDemo2(){
//避免反射破坏单例
throw new RuntimeException("无权访问该构造");
}
private static class Neibulei{
static final LazyDemo2 lazyDemo2=new LazyDemo2();
}
public static LazyDemo2 getInstance(){
return Neibulei.lazyDemo2;
}
}
枚举单例
枚举单例是一种饿汉模式的单例,它不会被反射和序列化破坏单例,是一种很安全的单例模式
public enum HungryDemo2 {
HUNGRY_DEMO_2;
public static HungryDemo2 getInstance(){
return HungryDemo2.HUNGRY_DEMO_2;
}
}