Java单例设计模式,多线程下同步
单例模式的特点:
- 只创建一次
- 私有的属性
- 公有的访问方法
单例模式的分类:
- 懒汉(LazySingleton);默认不自动实例化,等到用的时候根据当前情况实例化,并且返回当前对象;
- 恶汉(EagerSingleton);在类第一次加载的时候强制实例化;
用处:
一般用在配置文件的读取,有些东西只需要加载一次,以后重复使用。
示例代码:
- package myThread;
- /**
- * 单例模式,懒汉模式
- *
- * @author Champion.Wong
- *
- */
- public class LazySingleton {
- private static LazySingleton singleton = null;
- /**
- * 私有的构造方法,防止被多次实例化
- *
- */
- private LazySingleton() {
- }
- /**
- * 静态工厂,返回此类的唯一一个实例 判断,如果没有被实例化,则先初始化再返回
- *
- * @return LazySingleton
- */
- public static LazySingleton getInstance() {
- if (null == singleton)
- singleton = new LazySingleton();
- return singleton;
- }
- }
- package myThread;
- /**
- * 单例模式,饿汉模式
- * @author Champion.Wong
- *
- */
- public class EagerSingleton {
- private static EagerSingleton singleton = new EagerSingleton(); // 强制初始化,在类加载的时候就创建好了对象
- /**
- * 私有的构造方法,防止被多次实例化
- *
- */
- private EagerSingleton() {
- }
- public static EagerSingleton getInstance() {
- return singleton;
- }
- }
前边说过了,单例只创建一次,来做个测试,在单例的类中加入一个属性,当这个类在实例化的时候,会将当前的时间赋给这个属性。我们创建两个单例类的对象,然后分别拿出它们类中的这个属性进行对比。
所以,在EagerSingleton中加入属性:createdTime
- package myThread;
- /**
- * 单例模式,饿汉模式
- * @author Champion.Wong
- *
- */
- public class EagerSingleton {
- private static EagerSingleton singleton = new EagerSingleton(); // 强制初始化,在类加载的时候就创建好了对象
- /**
- * 私有的构造方法,防止被多次实例化
- *
- */
- private EagerSingleton() {
- createdTime = System.currentTimeMillis();
- }
- public static EagerSingleton getInstance() {
- return singleton;
- }
- private long createdTime = 0L; //此类的创建时间,在类实例化的时候赋值
- public long getCreatedTime() {
- return createdTime;
- }
- }
然后,写个Test:
- package myThread;
- public class Test_Singleton {
- public static void main(String[] args) {
- EagerSingleton singleton1 = EagerSingleton.getInstance();
- try {
- Thread.sleep(2000); //让当前线程睡眠2000ms,这样,两个对象实例化的时间就差2s
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- EagerSingleton singleton2 = EagerSingleton.getInstance();
- System.out.println("Singleton1:" + singleton1.getCreatedTime());
- System.out.println("Singleton2:" + singleton2.getCreatedTime());
- }
- }
最后控制台输出的内容为:
两个对象的实例化的时间相同,证明单例设想成功!
当多个线程访问单例类的时候,必须要保证同时只能由一个线程访问,当这个线程访问结束了,其它线程才能访问。这是就要给单例的访问方法加个同步锁(synchronized)。
修改后的LazySingleton为:
- package myThread;
- /**
- * 单例模式,懒汉模式
- *
- * @author Champion.Wong
- *
- */
- public class LazySingleton {
- private static LazySingleton singleton = null;
- /**
- * 私有的构造方法,防止被多次实例化
- *
- */
- private LazySingleton() {
- }
- /**
- * 静态工厂,返回此类的唯一一个实例 判断,如果没有被实例化,则先初始化再返回
- *
- * @return LazySingleton
- */
- synchronized public static LazySingleton getInstance() {
- if (null == singleton)
- singleton = new LazySingleton();
- System.out.println(Thread.currentThread().getName() + System.currentTimeMillis());
- try {
- Thread.sleep(900); //假设每个线程访问的时候都需要耗费一定的时间
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return singleton;
- }
- }
做个测试,起两个线程,没个线程中分别调用单例类的访问方法,每次访问的时候输出访问的时间,判断时间是否相等。如果相等,同步失败,如果不等,同步成功!
- package myThread;
- public class Test_Thread_Singleton {
- /**
- * @param args
- */
- public static void main(String[] args) {
- SingletonThread st = new SingletonThread();
- SingletonThread st1 = new SingletonThread();
- st.start();
- st1.start();
- }
- }
- class SingletonThread extends Thread {
- private LazySingleton singleton = null;
- public void run() {
- while (!this.isInterrupted()) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- singleton = LazySingleton.getInstance();
- }
- }
- }
看控制台的输出内容: