设计模式中单例模式实现方式

一,实现单例模式必要步骤

A,静态私有变量

B,私有构造方法,防止外部new

二,创建方式

1,饿汉模式一:静态常量。类加载的时候即创建一个实例,很着急,饿得很,上来不管吃得了吃不了,先来一个。

1) 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
2) 缺点:在类装载的时候就完成实例化,没有达到 Lazy Loading 的效果。如果从始至终从未使用过这个实例,则
会造成内存的浪费

3) 这种方式基于 classloder 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,在单例模式中大
多数都是调用 getInstance 方法,但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静
态方法)导致类装载,这时候初始化 instance 就没有达到 lazy loading 的效果
4) 结论:这种单例模式可用,可能造成内存浪费,但当你确定这个实例必定被用到时候,是可以使用的。

package designpattern.singleton.type1;

public class SingletonTest01 {
	public static void main(String[] args) {
		//单例的话,这两个实例应该是同一个,hashcode
		SingleTon instance = SingleTon.getInstance();
		SingleTon instance1 = SingleTon.getInstance();
		System.out.println(instance == instance1);
		//我们看下两个变量的hashcode
		System.out.println(instance.hashCode());
		System.out.println(instance1.hashCode());
	}
}
//饿汉式写法(静态变量)
class SingleTon{
	private static final SingleTon singleTon = new SingleTon();
	//首先将构造器私有构造器,外部不能new一个Singleton
	private SingleTon(){
		
	}
	//对外提供一个静态方法
	public static SingleTon getInstance(){
		return singleTon;
	}
}

 

2,饿汉模式二:静态代码块,使用静态代码块类加载的时候即创建一个实例。

1) 这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执
行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
2) 结论:这种单例模式可用,但是可能造成内存浪费

public class SingletonTest02 {
	public static void main(String[] args) {
		//单例的话,这两个实例应该是同一个,hashcode
		SingleTon2 instance = SingleTon2.getInstance();
		SingleTon2 instance1 = SingleTon2.getInstance();
		System.out.println(instance == instance1);
		//我们看下两个变量的hashcode
		System.out.println(instance.hashCode());
		System.out.println(instance1.hashCode());
	}
}
//饿汉模式2(静态代码块)
//其实就是在静态代码块给private 的实例初始化
class SingleTon2{
	private static final SingleTon2 singleTon;
	static{
		singleTon = new SingleTon2();
	}
	//首先将构造器私有构造器,外部不能new一个Singleton
	private SingleTon2(){
		
	}
	//对外提供一个静态方法调用获取该实例
	public static SingleTon2 getInstance(){
		return singleTon;
	}
}

3、懒汉模式(线程不安全):即我很懒,不着急。只有到用的时候,才去拿一个实例用

1) 起到了 Lazy Loading 的效果,但是只能在单线程下使用。
2) 如果在多线程下,一个线程进入了 if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过
了这个判断语句,这时便会 产生多个实例。所以在多线程环境下不可使用这种方式
3) 结论:在实际开发中, 不要使用这种方式.

代码如下:

//懒汉模式(线程不安全)
public class SingletonTest1 {
	public static void main(String[] args) {
		SingleTon instance = SingleTon.getInstance();
		SingleTon instance1 = SingleTon.getInstance();
		System.out.println(instance == instance1);
		System.out.println(instance.hashCode());
		System.out.println(instance1.hashCode());
	}
}
class SingleTon{
	private static SingleTon singleTon;

	private SingleTon() {
		
	}
	//提供一个静态方法,当需要使用这个对象,显式的调用才会创建
	public static SingleTon getInstance(){
		if(singleTon == null){
			singleTon = new SingleTon();
		}
		return singleTon;
	}
}

4、懒汉模式(线程安全),加入synchronized关键字

1) 解决了 线程安全问题
2) 效率太低了,每个线程在想获得类的实例时候,执行 getInstance()方法都要进行同步。而其实这个方法只执行
一次实例化代码就够了,后面的想获得该类实例,直接 return 就行了。 方法进行同步效率太低
3) 结论:在实际开发中, 不推荐使用这种方式

public class SingleTonTest3 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}

class SingleTon{
	private static SingleTon singleTon;

	private SingleTon() {
		
	}
	//提供一个静态方法,当需要使用这个对象,显示的调用才会创建
	//这里加了synchronized关键字,实现了线程安全,即多个线程同时访问时,排队访问该方法
	//这回导致排队耗时,降低性能
	public static synchronized SingleTon getInstance(){
		if(singleTon == null){
			singleTon = new SingleTon();
		}
		return singleTon;
	}
}

5、懒汉模式之同步代码块,即把实例的创建放到同步代码块中去

1) 解决了 线程安全问题
2) 效率太低了,每个线程在想获得类的实例时候,执行 getInstance()方法都要进行同步。而其实这个方法只执行
一次实例化代码就够了,后面的想获得该类实例,直接 return 就行了。 方法进行同步效率太低
3) 结论:在实际开发中, 不推荐使用这种方式

代码如下:

public static SingleTon getInstance(){
		if(singleTon == null){
			synchronized (SingleTon.class) {
				singleTon = new SingleTon();
			}
		}
		return singleTon;
}

6、懒汉模式之双重检查

1) Double-Check 概念是多线程开发中常使用到的,如代码中所示,我们进行了两次 if (singleton == null)检查,这
样就可以保证线程安全了。
2) 这样,实例化代码只用执行一次,后面再次访问时,判断 if (singleton == null),直接 return 实例化对象,也避
免的反复进行方法同步.
3) 线程安全; 延迟加载; 效率较高
4) 结论:在实际开发中,推荐使用这种单例设计模式

public class SingleTonTest4 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//单例的话,这两个实例应该是同一个
		SingleTon instance = SingleTon.getInstance();
		SingleTon instance1 = SingleTon.getInstance();
		System.out.println(instance == instance1);
		//我们看下两个变量的hashcode
		System.out.println(instance.hashCode());
		System.out.println(instance1.hashCode());
	}

}

class SingleTon {
	//volatile关键字,多线程访问时,首个访问到线程在赋值时候,其他线程可看到变化了的实例
	//此时,singleTon属性不为空,就不会再走同步代码块了
	private static volatile SingleTon singleTon;
	private SingleTon() {

	}
	// 提供一个静态方法,当需要使用这个对象,显示的调用才会创建
	// 这里加了synchronized关键字,实现了线程安全,即多个线程同时访问时,排队访问该方法
	// 这回导致排队耗时,降低性能
	public static SingleTon getInstance() {
		if (singleTon == null) {
			synchronized (SingleTon.class) {
				if (singleTon == null) {
					singleTon = new SingleTon();
				}
			}
		}
		return singleTon;
	}
}

7、静态内部类,使用java的类加载机制,实现懒加载和线程安全

1) 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。

2) 静态内部类方式在 Singleton 类被装载时并不会立即实例化,而是在需要实例化时,调用 getInstance 方法,才
会装载 SingletonInstance 类,从而完成 Singleton 的实例化。
3) 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM 帮助我们保证了线程的安全性,在类进行
初始化时,别的线程是无法进入的。
4) 优点: 避免了线程不安全,利用 静态内部类特点实现延迟加载,效率高
5) 结论: 推荐使用.

class SingleTon{
	//定义私有静态变量
	private static SingleTon singleTon;
	//构造器私有化是必须的
	private SingleTon(){
		
	}
	//写一个静态内部类,类里面定义一个静态属性SingleTon
	private static class SingleTonInstance{
		private static final SingleTon INSTANCE = new SingleTon();
	}
	
	public static synchronized SingleTon getInstance(){
		return SingleTonInstance.INSTANCE;
	}
}

8、通过枚举实现单例

1) 这借助 JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建
新的对象。
2) 这种方式是 Effective Java 作者 Josh Bloch 提倡的方式
3) 结论: 推荐使用

//通过枚举的方式
public class SingleTonTest6 {

	public static void main(String[] args) {
		//单例的话,这两个实例应该是同一个
		SingleTon instance = SingleTon.INSTANCE;
		SingleTon instance1 = SingleTon.INSTANCE;
		System.out.println(instance == instance1);
		//我们看下两个变量的hashcode
		System.out.println(instance.hashCode());
		System.out.println(instance1.hashCode());		
	}

}

enum SingleTon{
	//属性
	INSTANCE;
	
	public void sayOk(){
		System.out.println("this is ok");
	}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值