java设计模式之单例模式

1.概念

在它的核心结构中包含一个被称为单例的特殊类

2.特点

单例类自能有一个实例,构造方法限定为private避免了类在外部被实例化。

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

单例类必须给所有其他对象提供这一实例。

3.实现单例模式

1.饿汉式单例(立即加载方式)

解释:在类加载时就完成了初始化

package com.lpw.single;

// 饿汉式单例(一)
public class Singleton {
	
	// 私有无参构造函数(单例必须)
	private Singleton() {}
	
	private static Singleton singleton = new Singleton();
	
	public static Singleton getSingleton() {
		return singleton;
	}
	
}
package com.lpw.single;

// 饿汉式单例(二)所有静态的部分,在类加载的时候就会被实例化
public class Singleton2 {
	
	private Singleton2() {}
	
	private static Singleton2 singleton = null;
	
	// 静态代码块
	static {
		 singleton = new Singleton2();
	}
	
	public static Singleton2 getSingleton() {
		return singleton;
	}
}

饿汉式单例在类加载初始化时就创建好一个静态的对象供外部使用,除非系统重启,这个对象不会改变,所以本身就是线程安全的。

2.懒汉式单例(延迟加载方式)

解释:在类加载时不会被出示化

package com.lpw.single;

// 懒汉式单例
public class Singleton3 {
	
	// 不论何种单例,无参都是私有的避免被外部实例化
	private Singleton3() {}
	
	// 提供一个实例
	private static Singleton3 singleton = null;
	
	// 提供一个供外部调用的方法
	public static Singleton3 getSingleton() {
		
		if(singleton == null) {
			
			try {
				// 让线程休息10毫秒
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			singleton = new Singleton3();
		}
		
		return singleton;
	}
	
}
package com.lpw.single;

public class Test {
	
	public static void main(String[] args) {
		Singleton singleton = Singleton.getSingleton();
		
		Singleton3 singleton3 = Singleton3.getSingleton();
		
		
		// ----------------- 属于单线程------------------------
//		System.err.println(singleton3);
//		System.err.println(singleton3);
//		System.err.println(singleton3);
//		System.err.println(singleton3);
//		System.err.println(singleton3);
//		System.err.println(singleton3);
//		System.err.println(singleton3);
		
		// 单线程的时候,用的还是同一个对象
		// ----------------- 属于单线程结束------------------------
		
		
		
		// 多线程开始

		// 亲测,当让线程sleep10毫秒,在4g运存,i5,32位操作系统会出现不适同一个对象,在自己16g运存,i7,64位操作系统一直显示是同一个对象,估测由于运行太快导致
		
			// 第一个线程
			new Thread() {
				
				@Override
				public void run() {
					System.out.println(Singleton3.getSingleton());
				};
				
			}.start();
			
			// 第二个线程
			new Thread() {
				
				@Override
				public void run() {
					System.out.println(Singleton3.getSingleton());
				};
				
			}.start();
			
			// 第三个线程
			new Thread() {
				
				@Override
				public void run() {
					System.out.println(Singleton3.getSingleton());
				};
				
			}.start();
		// 多线程结束
	}
}

问题:该示例虽然用延迟加载方式实现了懒汉式单例,但在多线程环境下会产生多个single对象?

解决方法:在方法上加synchronized 同步锁此种方式虽然解决了多个实例对象问题,但是该方式运行效率低下,下一个进程想要获取对象,就必须等待上一个线程释放锁之后,才可以继续运行。

package com.lpw.single;

// 饿汉式单例(二)所有静态的部分,在类加载的时候就会被实例化
public class Singleton4 {
	
	private Singleton4() {}
	
	private static Singleton4 singleton = null;
	
	// 提供一个供外部调用的方法,使用同步锁解决多线程出现多个对象问题(加 static同步的是这个类,不加static 同步的是当前对象)
	
	// 使用同步锁会影响运行效率,同等条件下,同步锁,锁的内容越少,效率越高
	// 效率相对较低的方法
	synchronized public static Singleton4 getSingleton() {
		if(singleton==null) {
			singleton =new Singleton4();
		}
		return singleton;
	}
}

 使用同步锁会影响运行效率,同等条件下,同步锁,锁的内容越少,效率越高。所以,尝试缩小同步范围。

package com.lpw.single;

// 饿汉式单例(二)所有静态的部分,在类加载的时候就会被实例化
public class Singleton5 {
	
	private Singleton5() {}
	
	private static Singleton5 singleton = null;
	// 这时是可以的继续缩小
	public static Singleton5 getSingleton() {
		synchronized (Singleton5.class){
			if(singleton==null) {
				singleton =new Singleton5();
			}
			return singleton;
		}
	}
}
package com.lpw.single;

// 饿汉式单例(二)所有静态的部分,在类加载的时候就会被实例化
public class Singleton5 {
	
	private Singleton5() {}
	
	private static Singleton5 singleton = null;
	
	public static Singleton5 getSingleton() {
		
		if(singleton==null) {
			// 当 1,2,3三个线程走到这里,排队,线程1,创建一对象,线程2创建一对象,线程3创建一对象,此时逻辑错误
			synchronized (Singleton5.class){
				singleton =new Singleton5();
			}
		}
		return singleton;
	}
}
package com.lpw.single;

// 饿汉式单例(二)所有静态的部分,在类加载的时候就会被实例化
public class Singleton6 {
	
	private Singleton6() {}
	
	private static Singleton6 singleton = null;
	
	// 最终解决办法,使用双重检查进一步做了优化,可以避免整个方法被锁,支队需要锁的代码部分加锁,可以提高执行效率
	public static Singleton6 getSingleton() {
		
		if(singleton==null) {
			synchronized (Singleton6.class){
				if(singleton==null){
					singleton =new Singleton6();
				}
			}
		}
		return singleton;
	}
}

解决:使用双重检查进一步做了优化,可以避免整个方法被锁,支队需要锁的代码部分加锁,可以提高执行效率

3.静态内部类实现

package com.lpw.single2;

public class Singleton {
	
	private Singleton() {}
	
	// 静态内部类
	// 外部类:Singleton,被加载,内部类InSideClass没有被加载,除非主动被调用的时候才会被加载
	private static class InSideClass{
		private static Singleton singleton = new Singleton();
	}
	
	public static Singleton getSingleton() {
		return InSideClass.singleton;
	}
	
}

3.分析

优点:

1.在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。

2.避免对资源的多重占用(比如写文件操作)。

缺点:

扩展很困难。

4.应用场景

1.WEB中的计数器,不用每次刷新都在数据库加一次,用单例先缓存起来。

2.创建的一个对象需要消耗的资源过多,比如I/O于数据库的连接等。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值