设计模式之单例模式与多线程

46 篇文章 0 订阅
4 篇文章 0 订阅

单例模式确保某个类只有一个实例,并且自行实例化,并向整个系统提供这个实例

 

立即加载/饿汉模式

调用方法前,实例已经被创建

public class Single1 {
	//立即加载、饿汉模式
	private static Single1 single1=new Single1();
	//私有构造
	//定义为private,避免在外部被实例化
	private Single1() {}
	//静态工厂方法
	public static Single1 getSingle() {
		return single1;
	}
}
public class MyThread extends Thread{
	public void run() {
		System.out.println(Single1.getSingle().hashCode());
	}
}
public class Test {
	public static void main(String[] args) {
		MyThread thread1=new MyThread();
		MyThread thread2=new MyThread();
		MyThread thread3=new MyThread();
		thread1.start();
		thread2.start();
		thread3.start();
	}
}

 

延迟加载/懒汉模式

调用方法时,实例才被创建

public class Single2 {
	private static Single2 single2;
	private Single2() {};
	public static Single2 getSingle() {
		if(single2==null)
                single2=new Single2();
		return single2;
	}
}

以上代码,在多线程下,将会创建多个实例,与单例模式初衷相背离,是完全错误的。

如果稍加修改,就能看出端倪

public class Single2 {
	private static Single2 single2;
	private Single2() {};
	public static Single2 getSingle() {
		if(single2==null){
                   //这里让线程睡眠
                   try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			single2=new Single2();
		}
		return single2;
	}
}

 

延迟加载/懒汉模式的解决方案

1、使用 synchronized 同步标记

public class Single3 {
	private static Single3 single3;
	private Single3() {};
	synchronized public static Single3 getSingle() {
		if(single3==null)
			single3=new Single3();
		return single3;
	}
}

虽然保证了同一个实例,但是效率低下。

2、使用双重检查锁

public class Single4 {
	private static Single4 single4;
	private Single4() {};
	public static Single4 getSingle() {
		if(single4==null) {
			synchronized (Single4.class) {
				if(single4==null)
					single4=new Single4();
			}
		}
		return single4;
	}
}

使用双重检查锁,即保证了不会出现多个实例,又保证了线程执行的效率

 

使用静态内置类实现

public class Single5 {
	private static class Single5In{
		private static Single5 single5=new Single5();
	}
	private Single5() {}
	public static Single5 getSingle() {
		return Single5In.single5;
	}
}

 

序列化问题

如果遇到序列化对象时,使用默认的方式运行得到的结果还是多实例

public class Single5  implements Serializable{
	private static class Single5In{
		private static Single5 single5=new Single5();
	}
	private Single5() {}
	public static Single5 getSingle() {
		return Single5In.single5;
	}
 //	private Object readResolve() {
//		return Single5In.single5;
//	}
}
public class Test {
	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream("tempfile"));
		objectOutputStream.writeObject(Single5.getSingle());
		ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream("tempfile"));
		System.out.println(Single5.getSingle()==(Single5)objectInputStream.readObject());
		objectOutputStream.close();
		objectInputStream.close();
	}
}

得到的结果为false,即序列化破坏了单例。

原因是序列化会通过反射调用无参的构造方法创建一个新的对象

那么如何解决呢

事实上,将上面代码中的注释部分恢复即可

 

使用static静态代码块

public class Single6 {
	private static Single6 single6=null;
	private Single6(){};
	static {
		single6=new Single6();
	}
	public static Single6 getSingle() {
		return single6;
	}
}

 

使用enum枚举

 使用枚举类时,构造方法会被自动调用,因此可以用这一特性来实现单例模式。

public class Single7 {
	public enum MySingle {
		SINGLE;
		private Single7 single7;
		private MySingle() {
			single7=new Single7();
		}
		public Single7 getSingle() {
			return single7;
		}
	}
	private Single7 () {};
	public static Single7 getSingle() {
		return MySingle.SINGLE.getSingle();
	}
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值