Java设计模式-单例模式

一、饿汉式

1、静态常量

package singleton.type1;

public class TestSingleton01 {
	public static void main(String[] args) {
		Singleton instance1 = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance1 == instance2);
		System.out.println("instance1.hashCode = " + instance1.hashCode());
		System.out.println("instance2.hashCode = " + instance2.hashCode());
	}
}

class Singleton {
	//1.构造器私有化,不能通过new创建实例
	private Singleton(){
	}
	
	//2.类加载时就初始化实例
	private static final Singleton INSTANCE = new Singleton();
	
	//3.提供获取实例的静态方法
	public static Singleton getInstance(){
		return INSTANCE;
	}
}

2、静态代码块

package singleton.type2;

public class TestSingleton02 {
	public static void main(String[] args) {
		Singleton instance1 = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance1 == instance2);
		System.out.println("instance1.hashCode = " + instance1.hashCode());
		System.out.println("instance2.hashCode = " + instance2.hashCode());
	}
}

class Singleton {
	
	//1.构造器私有化
	private Singleton(){
	}
	
	//2.静态实例对象
	private static Singleton instance;
	
	//3.通过静态代码块实例化对象
	static {
		instance = new Singleton();
	}
	
	//4.提供静态的获取实例的方法
	public static Singleton getInstance(){
		return instance;
	}
}
结论:饿汉式虽然实现了单例,不存在线程安全问题,但因为在类加载时就创建了实例,如果在后续代码中没有使用到这个实例,可能造成内存浪费。

二、懒汉式

1、懒汉式写法1(存在线程安全问题)

package singleton.type3;

public class TestSingleton03 {
	public static void main(String[] args) {
		//模拟多线程下线程不安全问题
		new Thread(){
			public void run() {
				Singleton instance = Singleton.getInstance();
				System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
			};
			
		}.start();
		new Thread(){
			public void run() {
				Singleton instance = Singleton.getInstance();
				System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
			};
			
		}.start();
		new Thread(){
			public void run() {
				Singleton instance = Singleton.getInstance();
				System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
			};
			
		}.start();
		new Thread(){
			public void run() {
				Singleton instance = Singleton.getInstance();
				System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
			};
			
		}.start();
		new Thread(){
			public void run() {
				Singleton instance = Singleton.getInstance();
				System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
			};
			
		}.start();
		new Thread(){
			public void run() {
				Singleton instance = Singleton.getInstance();
				System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
			};
			
		}.start();
		new Thread(){
			public void run() {
				Singleton instance = Singleton.getInstance();
				System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
			};
			
		}.start();
		new Thread(){
			public void run() {
				Singleton instance = Singleton.getInstance();
				System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
			};
			
		}.start();
		new Thread(){
			public void run() {
				Singleton instance = Singleton.getInstance();
				System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
			};
			
		}.start();
		new Thread(){
			public void run() {
				Singleton instance = Singleton.getInstance();
				System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
			};
			
		}.start();
	}
}

class Singleton {
	
	//1.构造器私有化
	private Singleton(){
	}
	
	//2.静态实例对象
	private static Singleton instance;
	
	//3.在调用时实例化对象
	public static Singleton getInstance(){
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}

执行n次后出现线程安全问题
在这里插入图片描述

说明:
1)起到了Lazy Loading的效果,但是只能在单线程下使用。
2)在多线程下使用,会有线程安全问题。
3)线程安全问题如下:

当线程A进行if(instance==null),判断为null进入if后还没有实例化对象,此时线程B也进行了if(instance==null),判断null进入if,结果线程A和线程B创建了两个不同的实例。
在这里插入图片描述

结论:实际开发中不推荐使用。

2、懒汉式写法2(同步方法,线程安全)

package singleton.type4;

public class TestSingleton04 {
	public static void main(String[] args) {
		//模拟多线程下线程不安全问题
		new Thread(){
			public void run() {
				Singleton instance = Singleton.getInstance();
				System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
			};

		}.start();
		
		//其他线程复制上面的线程即可
	}
}

class Singleton {

	//1.构造器私有化
	private Singleton(){
	}

	//2.静态实例对象
	private static Singleton instance;

	//3.在调用时实例化对象,同步方法
	public static synchronized Singleton getInstance(){
		if(instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}
说明:
1)解决了线程安全问题
2)效率低

因为加了同步锁,当有线程进入方法获取实例时,其他线程都要在外面等待。而实际上是只要判断instance为null时才需要创建实例,instance不为null时,只需要return就行了。

结论:在实际开发中,不推荐使用。

3、懒汉式写法3(同步代码块,双重检查,线程安全)

package singleton.type5;

public class TestSingleton05 {
	public static void main(String[] args) {
		//模拟多线程下线程不安全问题
		new Thread(){
			public void run() {
				Singleton instance = Singleton.getInstance();
				System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
			};

		}.start();
		//其他线程复制上面的线程即可
	}
}

class Singleton {

	//1.构造器私有化
	private Singleton(){
	}

	//2.静态实例对象
	private static Singleton instance;

	//3.在调用时实例化对象
	public static Singleton getInstance(){
		if(instance == null) {
			//4.同步代码块
			synchronized(Singleton.class){
				//5.双重检查
				if(instance == null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}

说明:
1)解决了线程安全问题,提高了效率。
2)提高了效率

当线程A第一次判断instance为null时,进入if,再次判断instance为null,进入if,此时还没有创建实例,线程B第一次判断instance为null,进入if,由于线程A已经进入,加锁了,无法进行第二次if判断,此时线程A创建实例结束,线程B进入第二次if判断instance不为null,直接返回。当后续有线程获取实例时,只需要进行第一次if判断,instance不为null,就直接返回,提高了效率。
在这里插入图片描述

结论:实现Lazy Loading ,线程安全,效率高,实际开发中推荐使用。

三、静态内部类实现单例(线程安全,效率高)

package singleton.type6;

public class TestSingleton06 {
	public static void main(String[] args) {
		//模拟多线程下线程不安全问题
		new Thread(){
			public void run() {
				Singleton instance = Singleton.getInstance();
				System.out.println(Thread.currentThread().getName() + ": instance.hashCode = " + instance.hashCode());
			};

		}.start();
		
		//其他线程复制上面的线程即可
	}
}

class Singleton {

	//1.构造器私有化
	private Singleton(){
	}

	//2.静态内部类
	private static class Instance {
		private static final Singleton INSTANCE = new Singleton();
	}
	
	public static Singleton getInstance(){
		return Instance.INSTANCE;
	}
}
说明:
1)延迟加载

静态内部类并不会随着主类的加载而加载,而是在调用时才会加载,所以实现了LazyLoading。

2)线程安全

类的静态属性只会在类第一次加载时初始化,在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程时无法进入的。

结论:线程安全,延迟加载,效率高,实际开发中推荐使用。

四、枚举实现单例

package singleton.type7;

public class TestSingleton07 {
	public static void main(String[] args) {
		Singleton instance1 = Singleton.INSTANCE;
		Singleton instance2 = Singleton.INSTANCE;
		System.out.println(instance1 == instance2);
		System.out.println("instance1.hashCode = " + instance1.hashCode());
		System.out.println("instance2.hashCode = " + instance2.hashCode());
	}
}

enum Singleton {
	
	INSTANCE;
	
}
说明:
1)避免线程安全问题
2)防止反序列化重新创建对象。
结论:实际开发中推荐使用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值