主要思路:将构造方法私有化,并对外提供一个static的方法来创建对象
饿汉式单例
java
public class Hungry {
private Hungry(){
}
private final static Hungry hungry = new Hungry();
public static Hungry getInstance(){
return hungry;
}
public static void main(String[] args) {
Hungry hungry1 = Hungry.getInstance();
Hungry hungry2 = hungry.getInstance();
System.out.println(hungry1==hungry2);//true
}
}
缺点:一开始就创建对象,占用系统资源
懒汉式单例
java
public class Lazy {
private Lazy(){
System.out.println(Thread.currentThread().getName()+"ok");
}
private static Lazy lazy;
public static Lazy getInstance(){
if(lazy==null){
lazy = new Lazy();
}
return lazy;
}
public static void main(String[] args) {
for(int i=0;i<10;i++){
new Thread(()->{
Lazy.getInstance();
}).start();
}
}
}
单线程下不会出现问题,但多线程会会有并发问题,main方法的测试结果:
java
Thread-0ok
Thread-2ok
Thread-3ok
会发生同一时间创建了多个对象,所以出现了DCL双重检索
DCL懒汉式
java
public class Lazy {
private Lazy(){
System.out.println(Thread.currentThread().getName()+"ok");
}
private volatile static Lazy lazy; //volatile保证不会出现代码重排
public static Lazy getInstance(){
if(lazy==null) {
synchronized (Lazy.class) {
if (lazy == null) {
lazy = new Lazy();
/*
这个过程不是一个原子性,会出现代码重排现象
1.开配空间
2.执行构造方法
3.引用执行
*/
}
}
}
return lazy;
}
public static void main(String[] args) {
for(int i=0;i<10;i++){
new Thread(()->{
Lazy.getInstance();
}).start();
}
}
}
可以实现延迟实例化,并且是线程安全的
静态内部类
java
public class Holder {
private Holder(){
System.out.println(Thread.currentThread().getName()+"ok");
}
public static Holder getInstance(){
return InnerClass.holder;
}
public static class InnerClass{
private static final Holder holder = new Holder();
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
Holder.getInstance();
}).start();
}
}
}
反射破解单例模式
可以采用额外的变量进行控制,防止反射
java
public class Lazy {
private Lazy(){
synchronized (Lazy.class){
if(temp == false){
temp = true;
}else{
throw new RuntimeException("不要用反射破坏单例");
}
}
}
private volatile static Lazy lazy; //volatile保证不会出现代码重排
public static Lazy getInstance(){
if(lazy==null) {
synchronized (Lazy.class) {
if (lazy == null) {
lazy = new Lazy();
/*
这个过程不是一个原子性,会出现代码重排现象
1.开配空间
2.执行构造方法
3.引用指向
*/
}
}
}
return lazy;
}
public static void main(String[] args) throws Exception {
Lazy lazy = Lazy.getInstance();
Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
Lazy lazy1 = declaredConstructor.newInstance();
System.out.println(lazy==lazy1);
}
}
枚举单例
防止反射破坏
java
public enum EnumSingleton {
INSTANCE;
private EnumSingleton() {
}
public EnumSingleton getInstance(){
return INSTANCE;
}
public static void main(String[] args) throws Exception {
EnumSingleton instance = EnumSingleton.INSTANCE;
Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingleton instance1 = declaredConstructor.newInstance();
System.out.println(instance==instance1);
}
}
防止反序列化破坏
重写readResolve()方法
java
private Object readResolve() throws ObjectStreamException{
return singleton;
}