设计模式——单例模式

单例模式

单例模式属于创建型模式的一种,他的写法有许多中常见的有饿汉模式、懒汉模式、登记式模式(未去研究,不做说明)等。

一:单例模式特点
单例类只能有一个实例。
单例类必须自己创建自己的唯一实例。
单例类必须给所有其他对象提供这一实例。

二:单例模式分类
饿汉模式
懒汉模式
登记式模式

三:产生说明
我们平时在用类的对象实例时,使用new出来的,但是这样会出现不同的对象实例。
但是有时我们需要一个并且是唯一的一个对象实例,这样就总结出了单例模式。
如:
package com.zkrmfx.demo;
/*
 * 不是单例
 */
public class NoSingleton {

}
package com.zkrmfx.test;

import org.junit.Test;

import com.zkrmfx.demo.EagerSingleton;
import com.zkrmfx.demo.LazySingleton;
import com.zkrmfx.demo.MyThead;
import com.zkrmfx.demo.NoSingleton;

public class SingletonTest {

	@Test
	public void noSingletonTest() {
		NoSingleton singleton1 = new NoSingleton();
		NoSingleton singleton2 = new NoSingleton();
		System.out.println(singleton1);
		System.out.println(singleton2);
		// 测试结果 产生两个实例与我们要求产生一个实例需求不符
		// com.zkrmfx.demo.NoSingleton@7daf6ecc
		// com.zkrmfx.demo.NoSingleton@2e5d6d97
	}
}


正如上面测试结果那样,产生结果与我们需求不符。这就需要利用单例模式来产生对象实例了。

四:饿汉模式

package com.zkrmfx.demo;

/*
 * 饿汉式单例模式
 * 意义:利用空间去换时间
 * 缺点:产生对象不知道会不会使用,占用空间即内存
 */
public class EagerSingleton {

	// 不允许 new 出对象 ,构造方法私有化
	private EagerSingleton() {
	}

	// 自己产生对象
	private static EagerSingleton instance = new EagerSingleton();

	// 自己能提供反馈这个对象的方法并且只能是静态方法 用类点方法去调用
	public static EagerSingleton getInstance() {

		return instance;
	}
}

package com.zkrmfx.test;

import org.junit.Test;

import com.zkrmfx.demo.EagerSingleton;
import com.zkrmfx.demo.LazySingleton;
import com.zkrmfx.demo.MyThead;
import com.zkrmfx.demo.NoSingleton;

public class SingletonTest {

	@Test
	public void eagerSingletonTest() {
		EagerSingleton singleton1 = EagerSingleton.getInstance();
		EagerSingleton singleton2 = EagerSingleton.getInstance();
		System.out.println(singleton1);
		System.out.println(singleton2);
		// 测试结果 产生一个单一实例
		// com.zkrmfx.demo.EagerSingleton@5010be6
		// com.zkrmfx.demo.EagerSingleton@5010be6
	}
}

饿汉模式产生一个对象实例,由于他的对象实例是在类创建时就创建一个静态的对象实例。它将占用我们的内存,但是我们要用时随时可以去调用(即空间换时间)。
由于对象实例已经创建好,无论我们怎样去调用都是可以的,不存在线程安全问题。


五:懒汉式单列
package com.zkrmfx.demo;

/*
 * 懒汉式单例模式
 * 意义:对象在要用是才创建节约空间,即延迟加载策略
 * 缺点:会产生线程安全问题
 */
public class LazySingleton {

	// 不允许 new 出对象 ,构造方法私有化
	private LazySingleton() {

	}

	
	private static LazySingleton instance = null;

	// 自己能提供反馈这个对象的方法并且只能是静态方法 用类点方法去调用
	public static LazySingleton getInstance() {
		//如果为空,则new一个对象实例
		if (instance == null) {
			instance = new LazySingleton();
		}
		return instance;
	}
}
package com.zkrmfx.demo;
/*
 * 线程实现
 */
public class MyThead extends Thread {
	@Override
	public void run() {
		System.out.println(LazySingleton.getInstance());
	}
}

package com.zkrmfx.test;

import org.junit.Test;

import com.zkrmfx.demo.EagerSingleton;
import com.zkrmfx.demo.LazySingleton;
import com.zkrmfx.demo.MyThead;
import com.zkrmfx.demo.NoSingleton;

public class SingletonTest {
	
	@Test
	public void lazySingletonTest1() {
		LazySingleton lazySingleton1 = LazySingleton.getInstance();
		LazySingleton lazySingleton2 = LazySingleton.getInstance();
		System.out.println(lazySingleton1);
		System.out.println(lazySingleton2);
		// 测试结果 产生一个实例    !!!多测试几次
		// com.zkrmfx.demo.LazySingleton@5010be6
		// com.zkrmfx.demo.LazySingleton@5010be6
	}
	@Test
	public void lazySingletonTest2() {
		MyThead singleton1 = new MyThead();
		MyThead singleton2 = new MyThead();
		singleton1.start();
		singleton2.start();

		// 测试结果 产生多个实例    !!!多测试几次
		// com.zkrmfx.demo.LazySingleton@50229931
		// com.zkrmfx.demo.LazySingleton@d90d8b9
	}
}
从程序来看:我们在需要对象实例时才去创建,不想饿汉模式那样事先创建好,我们要用时才去创建,这样就避免了对象实例存在不使用的情况(延迟策略),但是时间相对来说要耗费的多一点。
lazySingletonTest1 测试结果:单线程测试时,对象实例是唯一
lazySingletonTest2 测试结果:多线程测试时,对象实例不唯一 
以上结果说明懒汉式单例不是线程安全的,那如何解决呢?

六:解决懒汉式线程安全问题
方案一:由于产生原因是多线程并发,给getInstance() 方法加同步防止并发的产生。
package com.zkrmfx.demo;

/*
 * 懒汉式单例模式
 * 意义:对象在要用是才创建节约空间,即延迟加载策略
 * 缺点:会产生线程安全问题
 */
public class LazySingleton {

	// 线程安全解决方案一:  加synchronized同步
	// 不允许 new 出对象 ,构造方法私有化
	private LazySingleton() {

	}

	// 自己产生对象
	private static LazySingleton instance = null;

	// 自己能提供反馈这个对象的方法并且只能是静态方法 用类点方法去调用 加入synchronized 同步
	public static synchronized LazySingleton getInstance() {
		//如果不存在,则new一个
		if (instance == null) {
			instance = new LazySingleton();
		}
		return instance;
	}
}
package com.zkrmfx.test;

import org.junit.Test;

import com.zkrmfx.demo.EagerSingleton;
import com.zkrmfx.demo.LazySingleton;
import com.zkrmfx.demo.MyThead;
import com.zkrmfx.demo.NoSingleton;

public class SingletonTest {

	@Test
	public void synchronizedLazySingletonTest() {
		MyThead singleton1 = new MyThead();
		MyThead singleton2 = new MyThead();
		singleton1.start();
		singleton2.start();
		// 测试结果 产生一个实例 !!!多测试几次
		// com.zkrmfx.demo.LazySingleton@479f9b98
		// com.zkrmfx.demo.LazySingleton@479f9b98
	}
}


方案二: 利用静态内部类加载时只加载一次从而只产生一个对象实例

package com.zkrmfx.demo;

/*
 * 懒汉式单例模式
 * 意义:对象在要用是才创建节约空间,即延迟加载策略
 * 缺点:会产生线程安全问题
 */
public class LazySingleton {

	// 线程安全解决方案二-----静态内部类
	// 不允许 new 出对象 ,构造方法私有化
	private LazySingleton() {

	}

	// 利用类加载时只 加载一次 从而只产生一个实例
	private static class Lazy {
		private static final LazySingleton INSTANCE = new LazySingleton();
	}

	// 自己能提供反馈这个对象的方法并且只能是静态方法 用类点方法去调用
	public static LazySingleton getInstance() {

		return Lazy.INSTANCE;
	}
}
package com.zkrmfx.test;

import org.junit.Test;

import com.zkrmfx.demo.EagerSingleton;
import com.zkrmfx.demo.LazySingleton;
import com.zkrmfx.demo.MyThead;
import com.zkrmfx.demo.NoSingleton;

public class SingletonTest {

	@Test
	public void staticClassEagerSingletonTest() {
		MyThead singleton1 = new MyThead();
		MyThead singleton2 = new MyThead();
		singleton1.start();
		singleton2.start();
		// 测试结果 产生一个实例 !!!多测试几次
		// com.zkrmfx.demo.LazySingleton@4e08c2b2
		// com.zkrmfx.demo.LazySingleton@4e08c2b2
	}
}

方案三:添加双重检查锁定 (这是别人总结的东西 详细可以连接: http://blog.csdn.net/jason0539/article/details/23297037/
package com.zkrmfx.demo;

/*
 * 懒汉式单例模式
 * 意义:对象在要用是才创建节约空间,即延迟加载策略
 * 缺点:会产生线程安全问题
 */
public class LazySingleton {

	// 线程安全解决方案三 : 双重检查锁定
	// 双重检查锁定不是线程安全的,要用volatile关键字
	// 不允许 new 出对象 ,构造方法私有化
	private LazySingleton() {
	}

	// 未加volatile
	private static LazySingleton instance = null;

	// volatile修饰一个变量,而且是一个"容易变"的变量。
	// 在每次取这个变量值的时候,要求不是取它上次在某个时候取
	// 的临时缓存变量(比如说暂存在某个寄存器中),而是直接到内存中取。
	// volatile变量能防止优化,别如说你在某个地方可能连续调用了好几
	// 次这个函数,于是编译器优化后,可能就调用一次,其他几次就采用这一
	// 次调用的返回值,而volatile修饰后,要让每一次都进行函数调用,
	// 而不采用暂存值。
	// 详细请链接 http://blog.csdn.net/jk198310/article/details/14222713

	// private static volatile LazySingleton instance = null;
	// 自己能提供反馈这个对象的方法并且只能是静态方法 用类点方法去调用
	public static LazySingleton getInstance() {
		if (instance == null) {
			synchronized (LazySingleton.class) {
				if (instance == null) {
					instance = new LazySingleton();
				}
			}
		}
		return instance;
	}
}
package com.zkrmfx.test;

import org.junit.Test;

import com.zkrmfx.demo.EagerSingleton;
import com.zkrmfx.demo.LazySingleton;
import com.zkrmfx.demo.MyThead;
import com.zkrmfx.demo.NoSingleton;

public class SingletonTest {

	@Test
	public void doubleCheckedLockingEagerSingletonTest1() {
		MyThead singleton1 = new MyThead();
		MyThead singleton2 = new MyThead();
		singleton1.start();
		singleton2.start();
		// 测试结果 产生一个单一实例 
		// com.zkrmfx.demo.LazySingleton@7daf6ecc
		// com.zkrmfx.demo.LazySingleton@7daf6ecc
	}
	@Test
	public void doubleCheckedLockingEagerSingletonTest2() {
		EagerSingleton singleton1 = EagerSingleton.getInstance();
		EagerSingleton singleton2 = EagerSingleton.getInstance();
		System.out.println(singleton1);
		System.out.println(singleton2);
		// 测试结果 产生一个单一实例 
		// com.zkrmfx.demo.LazySingleton@7daf6ecc
		// com.zkrmfx.demo.LazySingleton@7daf6ecc
	}	
}

这种方式我找不到用不用volatile的区别,帖子没有给具体测试的方式,说的比较笼统。
我用了几种方法测试,都不能测出来 volatile 具体使用方式我还是没能搞清楚
不知道问题出在哪儿,还是自己知识面太窄了!!!
不知道缓存的问题望大神能给出测试的方法

六:单例模式应用
在Java jdk 中 Runtime 类就是单列类 getRuntime() 返回唯一对象实例
不要错误的认为利用类点 getInstance() 调用对象实例的就是单例类,Java jdk 中 Calenda r类就是getInstance()返回对象实例的,但不是单例类。

以下是我找到的双重检查锁定帖子说明:(看下一别的大神的解释)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值