一、场景简述
单例模式下有饿汉模式和懒汉模式,其中懒汉模式在于调用相关方法时实例才被创建。懒汉模式我们不难实现,但是在懒汉模式下我们如果使用多线程,就会取出多个实例的情况,与单例模式相违背,所以该篇博客笔者主要关于在多线程环境下利用DCL双检查锁机制来实现懒汉模式。
二、场景实现
1、多线程环境下的懒汉模式实现“错误的单例模式”
MyObject类
package singleton;
/**
* @author: linjie
* @description: 懒汉模式
* @create: 2018/10/07 13:29
*/
public class MyObject {
private static MyObject myObject;
private MyObject(){}
public static MyObject getInstance(){
try {
//懒汉模式
if (myObject != null){
}else {
//模拟创建对象之前做的准备性工作
Thread.sleep(3000);
myObject = new MyObject();
}
}catch (InterruptedException e){
e.printStackTrace();
}
return myObject;
}
}
MyThread类
package singleton;
/**
* @author: linjie
* @description: 线程类
* @create: 2018/10/07 13:32
*/
public class MyThread extends Thread{
@Override
public void run(){
//打印hashcode值
System.out.println(MyObject.getInstance().hashCode());
}
}
Run启动类(实现多个线程)
package singleton;
/**
* @author: linjie
* @description:启动类
* @create: 2018/10/07 13:34
*/
public class Run {
public static void main(String[] args){
MyThread myThread = new MyThread();
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread.start();
myThread1.start();
myThread2.start();
}
}
运行结果,可以看到是不同的hashcode值,违背了单例模式
2、使用synchronized同步方法/同步代码块实现多线程下的懒汉模式
只需修改MyObject类
使用同步方法
package singleton;
/**
* @author: linjie
* @description: 懒汉模式
* @create: 2018/10/07 13:29
*/
public class MyObject {
private static MyObject myObject;
private MyObject(){}
synchronized public static MyObject getInstance(){
try {
//懒汉模式
if (myObject != null){
}else {
//模拟创建对象之前做的准备性工作
Thread.sleep(3000);
myObject = new MyObject();
}
}catch (InterruptedException e){
e.printStackTrace();
}
return myObject;
}
}
使用同步代码块
package singleton;
/**
* @author: linjie
* @description: 懒汉模式
* @create: 2018/10/07 13:29
*/
public class MyObject {
private static MyObject myObject;
private MyObject(){}
public static MyObject getInstance(){
try {
synchronized (MyObject.class){
if (myObject != null){
}else {
//模拟创建对象之前做的准备性工作
Thread.sleep(3000);
myObject = new MyObject();
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
return myObject;
}
}
然而通过以上两种方法的确实现了单例,获取的hashcode也是同一个值,但是他们这样的写法,是全部代码都是同步的,这就大大降低了运行效率,所以才有某些重要的代码进行单独的同步,而其他代码则不需要同步,这样运行效率可以大大提升,但是会导致无法解决得到同一个实例对象的结果,如下
package singleton;
/**
* @author: linjie
* @description: 懒汉模式
* @create: 2018/10/07 13:29
*/
public class MyObject {
private static MyObject myObject;
private MyObject(){}
public static MyObject getInstance(){
try {
//懒汉模式
if (myObject != null){
}else {
//模拟创建对象之前做的准备性工作
Thread.sleep(3000);
//虽然部分代码被上锁,但还是有非线程安全的问题
synchronized (MyObject.class){
myObject = new MyObject();
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
return myObject;
}
}
运行结果,即使效率提升,但还是违背了单例模式
3、使用DCL双检查锁机制实现多线程下的懒汉模式
所以我们就使用DCL双检查锁机制来实现多线程环境下的懒汉模式了
MyObject类
package singleton;
/**
* @author: linjie
* @description: 此版本代码称为双重检查 Double-Check Locking
* @create: 2018/10/07 13:50
*/
public class MyObject {
private static MyObject myObject;
private MyObject(){}
public static MyObject getInstance(){
try {
if (myObject != null){
}else {
//模拟创建对象之前做的准备性工作
Thread.sleep(3000);
//同步代码块中还有一层判断
synchronized (MyObject.class){
if (myObject == null){
myObject = new MyObject();
}
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
return myObject;
}
}
MyThread类
package singleton;
/**
* @author: linjie
* @description: 线程类
* @create: 2018/10/07 13:32
*/
public class MyThread extends Thread{
@Override
public void run(){
//打印hashcode值
System.out.println(MyObject.getInstance().hashCode());
}
}
Run启动类
package singleton;
/**
* @author: linjie
* @description:启动类
* @create: 2018/10/07 13:34
*/
public class Run {
public static void main(String[] args){
MyThread myThread = new MyThread();
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread.start();
myThread1.start();
myThread2.start();
}
}
运行结果,可以看到使用了DCL双检查机制后,实现了单例的结果,并且是部分代码同步,大大提升了运行效率,何乐而不为。
三、参考文献
《Java Multi-thread Programming》