单例模式和多线程

本文探讨了单例模式在多线程环境下的问题,包括立即加载的饿汉模式和延迟加载的懒汉模式。针对懒汉模式在多线程中可能导致的多个实例问题,提出了加锁同步的解决方案,如使用`synchronized`关键字和双重检查锁定(DCL)。同时,介绍了其他实现单例模式的方法,如静态内置类、静态代码块和枚举类型。
摘要由CSDN通过智能技术生成

单例类只能存在一个实例,单例类必须自己创建自己唯一的实例,单例类必须能给其他的所有对象提供这一实例。

单例模式主要包括立即加载/饿汉模式、延迟加载/懒汉模式等。

单例模式和多线程在一起时,就会出现问题,比如多个线程可以创建多个单例实例,这样是违背单例模型的初衷的。

一、立即加载/饿汉模式

1.立即加载即在调用方法前,实例已经被创建了,下面看一个实现立即加载和多线程的单例设计模式。

MyObject.java

/*
 * 单例模式-》饿汉模式,记载调用方法前,实例已经被创建
 */
public class MyObject {
private static MyObject myObject=new MyObject();
	private MyObject() {}
    public static MyObject getInstance(){
		return myObject;
    }
}
Run.java
public class Run {
static Runnable t1=new Runnable(){
	@Override
	public void run() {
		System.out.println(MyObject.getInstance().hashCode());
	}
};
public static void main(String[] args){
	Thread thread1=new Thread(t1);
	Thread thread2=new Thread(t1);
	Thread thread3=new Thread(t1);
	thread1.start();
	thread2.start();
	thread3.start();
}
}
结果如下:


可以看到hashCode是同一个数值,说明是同一个对象。

二、延迟加载/懒汉模式

延迟加载是在调用get()方法时实例才被创建,常见的实现方法就是在get()方法中进行new 实例化。

但是延迟加载的缺点就是在多线程的环境中,会取出多个实例的情况,这样与单例模式的初衷是相悖的。

MyObject1.java

/*
 * 延迟加载即懒汉模式在和多线程结合的时候,当有多个线程是就会创建出3个对象,这样完全不符合单例模式
 */
public class MyObject1 {
private static MyObject1 object;
	private MyObject1() {}
 public static MyObject1 getInstace() throws InterruptedException{
    	if(object!=null){}
    	else{
    		Thread.sleep(2000);
    		object=new MyObject1();
    	}
		return object;
    }
}
Run1.java
public class Run1 {
static Runnable t1=new Runnable() {
	@Override
	public void run() {
		try {
			System.out.println(MyObject1.getInstace().hashCode());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
};
public static void main(String[] args){
	Thread thread1=new Thread(t1);
	Thread thread2=new Thread(t1);
	thread1.start();
	thread2.start();
}
}
结果如下:


可以看出两个hashCode值,说明是两个对象,与单例模式不符。

2.解决延迟加载缺点的方法

(1)对getInstance()方法声明synchronized关键字 synchronized public static MyObject1 getInstace()

(2)使用同步代码块 synchronized(MyObject.class)

(3)针对某些重要的代码块进行单独的同步

第一二种方法确实能解决问题,但是效率太慢,第三种方法能解决效率问题,但是还是没法解决懒汉模式遇到多线程的问题。

最好的解决方案即用户DCL双检查锁机制,即在同步代码块之前检查一次,在同步代码块中再检查一次。

MyObject2.java

/*
 * 使用DCL双检查锁机制来实现多线程环境中的延迟加载单例设计模式
 */
public class MyObject2 {
private static MyObject2 myObject2;
	private MyObject2() {}
    public static MyObject2 getInstance() throws InterruptedException{
    	if(myObject2!=null){}
    	else{
    		Thread.sleep(2000);
    		synchronized (MyObject2.class) {
				if(myObject2==null){
					myObject2=new MyObject2();
				}
			}
    	}
    	return myObject2;
    }
}
Run.java
public class Run2 {
static Runnable t1=new Runnable() {
		@Override
	public void run() {
		try {
			System.out.println(MyObject2.getInstance().hashCode());
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
};
public static void main(String[] args){
	Thread thread1=new Thread(t1);
	Thread thread2=new Thread(t1);
	thread1.start();
	thread2.start();
}
}
结果如下:



可以看出是一个对象。

三、其他方法实现单例模式

(1)使用静态内置类实现单例模式

MyObject3.java

/*
 * 使用静态内置类实现单例多线程
 */
public class MyObject3 {
private static class MyObject3Hander{
	private static MyObject3 myObject3=new MyObject3();
}
	private MyObject3() {}
	public static MyObject3 getInstance(){
		return MyObject3Hander.myObject3;
	}

}

(2)使用static代码块实现单例模式

MyObject4.java

/*
 * 使用static静态代码块实现单例模式
 */
public class MyObject4 {
private static MyObject4 instance=null;
	private MyObject4() {
		// TODO Auto-generated constructor stub
	}
static{
	instance=new MyObject4();
}
public static MyObject4 getInstance(){
	return instance;
}
}

(3)使用enum数据雷翔实现单例模式

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值