欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java设计模式之单例模式

程序员文章站 2022-07-14 10:04:19
...

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于数据库的连接等。

 

 

相关标签: 单例模式