单例模式分享演示

原文链接https://blog.csdn.net/qq_35860138/article/details/86477538

一. 什么是单例模式

因进程需要,有时我们只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计。

二. 单例模式的特点

1、单例模式只能有一个实例。

2、单例类必须创建自己的唯一实例。

3、单例类必须向其他对象提供这一实例。

三. 单例模式VS静态类

在知道了什么是单例模式后,我想你一定会想到静态类,“既然只使用一个对象,为何不干脆使用静态类?”,这里我会将单例模式和静态类进行一个比较。

1、单例可以继承和被继承,方法可以被override,而静态方法不可以。

2、静态方法中产生的对象会在执行后被释放,进而被GC清理,不会一直存在于内存中。

3、静态类会在第一次运行时初始化,单例模式可以有其他的选择,即可以延迟加载。

4、基于2, 3条,由于单例对象往往存在于DAO层(例如sessionFactory),如果反复的初始化和释放,则会占用很多资源,而使用单例模式将其常驻于内存可以更加节约资源。

5、静态方法有更高的访问效率。

6、单例模式很容易被测试。

几个关于静态类的误解:

误解一:静态方法常驻内存而实例方法不是。

实际上,特殊编写的实例方法可以常驻内存,而静态方法需要不断初始化和释放。

误解二:静态方法在堆(heap)上,实例方法在栈(stack)上。

实际上,都是加载到特殊的不可写的代码内存区域中。

静态类和单例模式情景的选择:

情景一:不需要维持任何状态,仅仅用于全局访问,此时更适合使用静态类。

情景二:需要维持一些特定的状态,此时更适合使用单例模式。

四. 单例模式的实现

import java.util.concurrent.atomic.AtomicReference;

/**
*

  • 类型:
  • 懒汉式单例:使用时创建。考虑线程安全、优化加锁方式
  • 饿汉式单例:类加载时创建。考虑对象大小
  • @author Administrator

*/
//懒汉模式(线程不安全)

class Singleton1{
	 private Singleton1(){
		System.out.println("产生01实例");
	}
	private static Singleton1 instance;
	public static Singleton1 getInstance(){
		if(instance==null){
			instance=new Singleton1();
		}
		return instance;
	}
}

//懒汉模式加锁(线程安全)大对象,少用,延迟加载

class Singleton2{
	private Singleton2(){
		
		System.out.println("产生02实例");
	}
	/**
	 * volatile 属性修改,立即可见。不能保证原子性。
	 */
	private static volatile Singleton2 instance;
	public static synchronized Singleton2 getInstance(){
		if(instance==null){
				if(instance==null){
					instance=new Singleton2();
				}
		}
		return instance;
	}
}

//饿汉模式(线程安全),小对象,频繁用。
//直接在运行这个类的时候进行一次loading,之后直接访问。显然,这种方法没有起到lazy loading的效果,考虑到前面提到的和静态类的对比,这种方法只比静态类多了一个内存常驻而已。

class Singleton3{
	private Singleton3(){
		System.out.println("产生03实例");
	}
	private static Singleton3 instance=new Singleton3();
	public static Singleton3 getInstance(){
		return instance;
	}
}

//静态类内部加载(线程安全/懒汉)
//使用内部类的好处是,静态内部类不会在单例加载时就加载,而是在调用getInstance()方法时才进行加载,达到了类似懒汉模式的效果,而这种方法又是线程安全的。

class Singleton4{//
	private Singleton4(){
		System.out.println("产生04实例");
	}
	/**
	 * 内部类 延迟加载
	 */
	static class Inner{
	private static Singleton4 instance = new Singleton4();
	}
	public static Singleton4 getInstance(){
		
		return Inner.instance;
	}
}

/**

  • enum枚举类
  • 通常用于定义一些固定的常量值
  • 例如:enum gender{
  • MALE,FEMALE
  • };
  • 这两个值通常会认为是2个对象实例

(1)自由串行化。(2)保证只有一个实例。(3)线程安全。
枚举单例这种方法问世以来,许多分析文章都称它是实现单例的最完美方法——写法超级简单,而且又能解决大部分的问题。避免反射攻击,避免序列化问题
不过我个人认为这种方法虽然很优秀,但是它仍然不是完美的——比如,在需要继承的场景,它就不适用了
*/

enum Singleton5{
	instance;//单例对象(饿汉,线程安全,类加载时创建,且只创建一次
	//构造方法默认无参且私有
	private Singleton5(){
		System.out.println("产生05实例");
	}
	public static Singleton5 getInstance() {
		return instance;
	}
	public void otherMethods(){
		System.out.println("其他方法");
	}
}

//双重校验锁法(懒汉,通常线程安全,低概率不安全
/**

  • 接下来我解释一下在并发时,双重校验锁法会有怎样的情景:

STEP 1. 线程A访问getInstance()方法,因为单例还没有实例化,所以进入了锁定块。

STEP 2. 线程B访问getInstance()方法,因为单例还没有实例化,得以访问接下来代码块,而接下来代码块已经被线程1锁定。

STEP 3. 线程A进入下一判断,因为单例还没有实例化,所以进行单例实例化,成功实例化后退出代码块,解除锁定。

STEP 4. 线程B进入接下来代码块,锁定线程,进入下一判断,因为已经实例化,退出代码块,解除锁定。??

STEP 5. 线程A获取到了单例实例并返回,线程B没有获取到单例并返回Null。

理论上双重校验锁法是线程安全的,并且,这种方法实现了lazyloading。

*/

class Singleton6{//大对象,少用,延迟加载
	private Singleton6(){
		System.out.println("产生06实例");
	}
	/**
	 * volatile 属性修改,立即可见。不能保证原子性。
	 */
	private static Singleton6 instance;
	public static Singleton6 getInstance(){
		if(instance==null){
			synchronized (Singleton6.class) {
				if(instance==null){
					instance=new Singleton6();
				}
			}
		}
		return instance;
	}
}

//双重校验锁法volatile版(懒汉,线程安全

class Singleton7{
	private Singleton7(){
		System.out.println("产生07实例");
	}
	/**
	 * volatile 属性修改,立即可见。不能保证原子性。在它的赋值完成之前,就不用会调用读操作
	 */
	private static volatile Singleton7 instance;
	public static Singleton7 getInstance(){
		if(instance==null){
			synchronized (Singleton7.class) {
				if(instance==null){//在这等待写操作完成,后读数据判断
					instance=new Singleton7();
				}
			}
		}
		return instance;
	}
}

/**

  • ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。
  • 对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,
  • 而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,
  • 让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。
    */
class Singleton8{
	private Singleton8(){
		System.out.println("产生单例08");
	}
	/**
	 *能够将某个对象绑定到当前线程,通过此类可以实现线程内部单例
	 *常用方法set绑定
	 *get获取绑定
	 *renmove移除绑定 
	 */
	private static ThreadLocal<Singleton8> threadLocal = new ThreadLocal<>();
	public static Singleton8 getInstance(){
		Singleton8 looper = threadLocal.get();
		if(looper==null){
			looper = new Singleton8();
			threadLocal.set(looper);
		}
		return looper;
	}
	public void remove(){
		threadLocal.remove();
	}
}
class Singleton9{
	private Singleton9(){
		System.out.println("产生单例09");
	}
 
	private static AtomicReference<Singleton9> instance = new AtomicReference<Singleton9>();
	public static Singleton9 getInstance(){
		for(;;) {
			Singleton9 current = instance.get();
			if(current!=null) {
				return current;
			}
			current=new Singleton9();
			if (instance.compareAndSet(null, current)) {
				return current;
			}
		}
	}
}

//--------------------------------------

public class TestSingleton01 {
	public static void main(String[] args) {
		for (int i = 0; i < 4; i++) {
			new Thread(){
				@Override
				public void run() {
					doMethod09();
					//Singleton5.instance.otherMethods();
				}
			}.start();
			
		}
	}

//-------------------------------------------


```java
private static void doMethod01() {
		
		Singleton1 t1 = Singleton1.getInstance();
		Singleton1 t2 = Singleton1.getInstance();
		System.out.println(t1);
		System.out.println(t2);
		boolean isSingleton = t1.equals(t2);
		System.out.println(isSingleton);
}

private static void doMethod02() {
	
	Singleton2 t1 = Singleton2.getInstance();
	Singleton2 t2 = Singleton2.getInstance();
	System.out.println(t1);
	System.out.println(t2);
	boolean isSingleton = t1.equals(t2);
	System.out.println(isSingleton);
}
private static void doMethod03() {
	
	Singleton3 t1 = Singleton3.getInstance();
	Singleton3 t2 = Singleton3.getInstance();
	System.out.println(t1);
	System.out.println(t2);
	boolean isSingleton = t1.equals(t2);
	System.out.println(isSingleton);
}
private static void doMethod04() {
	
	Singleton4 t1 = Singleton4.getInstance();
	Singleton4 t2 = Singleton4.getInstance();
	System.out.println(t1);
	System.out.println(t2);
	boolean isSingleton = t1.equals(t2);
	System.out.println(isSingleton);
}
private static void doMethod05() {
	
	Singleton5 t1 = Singleton5.getInstance();
	Singleton5 t2 = Singleton5.getInstance();
	System.out.println(t1);
	System.out.println(t2);
	boolean isSingleton = t1.equals(t2);
	System.out.println(isSingleton);
}
private static void doMethod06() {
	
	Singleton6 t1 = Singleton6.getInstance();
	Singleton6 t2 = Singleton6.getInstance();
	System.out.println(t1);
	System.out.println(t2);
	boolean isSingleton = t1.equals(t2);
	System.out.println(isSingleton);
}
private static void doMethod07() {
	
	Singleton7 t1 = Singleton7.getInstance();
	Singleton7 t2 = Singleton7.getInstance();
	System.out.println(t1);
	System.out.println(t2);
	boolean isSingleton = t1.equals(t2);
	System.out.println(isSingleton);
}
private static void doMethod08() {
	
	Singleton08 l2 = Singleton08.getInstance();
	System.out.println("l2"+l2);
	Singleton08 l1 = Singleton08.getInstance();
	System.out.println("l1"+l1);
	l1.remove();
	l2.remove();
}
private static void doMethod09() {
	Singleton9 t1 = Singleton9.getInstance();
	Singleton9 t2 = Singleton9.getInstance();
	System.out.println(t1);
	System.out.println(t2);
	boolean isSingleton = t1.equals(t2);
	System.out.println(isSingleton);	
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值