解决懒汉单例模式线程安全问题
1, 懒汉单例模式
package SingleTonTest;
/**
* @Name: 懒汉单例设计模式
* @Author:ZYJ
* @Date:2019-07-25-20:55
* @Description:
*/
class Singleton{
private static Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
}
public class SingleTonTest {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 =Singleton.getInstance();
System.out.println(singleton1==singleton2);
}
}
懒汉单例模式,也叫延迟加载。只有在调用getInstance()
时才会创建实例,在多线程并发下容易产生线程安全问题
2, 多线程状态下懒汉单例模式
package SingleTonTest;
/**
* @Name: 多线程状态下懒汉单例模式
* @Author:ZYJ
* @Date:2019-07-26-11:45
* @Description:
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if(singleton==null){
try {
Thread.sleep(1000);
singleton=new Singleton();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return singleton;
}
public static void main(String[] args) {
MyThread myThread =new MyThread();
Thread thread1 = new Thread(myThread);
Thread thread2 = new Thread(myThread);
Thread thread3 = new Thread(myThread);
thread1.start();
thread2.start();
thread3.start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Singleton.getInstance().hashCode());
}
}
从打印结果可以看出,多线程下产生的并不是同一个对象
3 ,解决办法
3.1 使用synchronized 同步方法
package SafeSingleTon1;
/**
* @Name: 解决多线程下懒汉单例模式的线程安全问题
* @Author:ZYJ
* @Date:2019-07-26-12:14
* @Description: 方法一 :使用synchronized 同步方法
*/
public class SafeSingleTonTest {
public static void main(String[] args) {
MyThread myThread =new MyThread();
Thread thread1 =new Thread(myThread);
Thread thread2 =new Thread(myThread);
Thread thread3 =new Thread(myThread);
thread1.start();
thread2.start();
thread3.start();
}
}
class SingleTon{
private static SingleTon singleTon;
private SingleTon(){}
public static synchronized SingleTon getInstance(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(singleTon==null){
singleTon= new SingleTon();
}
return singleTon;
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(SingleTon.getInstance().hashCode());
}
}
问题:此方法会带来大量的CPU开销
3.2 使用Synchronized 同步代码块
package SafeSingleTon2;
/**
* @Name: 解决多线程下懒汉单例模式的线程安全问题
* @Author:ZYJ
* @Date:2019-07-26-12:25
* @Description: 方法二: 使用synchronized同步代码块
*/
public class SafeSingleTonTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread1 = new Thread(myThread);
Thread thread2 = new Thread(myThread);
Thread thread3 = new Thread(myThread);
thread1.start();
thread2.start();
thread3.start();
}
}
class SingleTon {
private static SingleTon singleton;
private SingleTon() {}
public static SingleTon getInstance() {
try {
Thread.sleep(1000);
if (singleton == null) {
synchronized (SingleTon.class) {
if(singleton == null){
singleton = new SingleTon();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return singleton;
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(SingleTon.getInstance().hashCode());
}
}
问题:效率较低
3.3 使用DCL(Double—Check Locking)双重查锁机制
package SafeSingleTon3;
/**
* @Name: 解决多线程下懒汉单例模式的线程安全问题
* @Author:ZYJ
* @Date:2019-07-26-12:45
* @Description:方法三:双重加锁问题
*/
public class SafeSingleTonTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread1 =new Thread(myThread);
Thread thread2 =new Thread(myThread);
Thread thread3 =new Thread(myThread);
thread1.start();
thread2.start();
thread3.start();
}
}
class SingleTon {
private static volatile SingleTon singleTon;
private SingleTon() {}
public static SingleTon getInstance() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (singleTon == null) {
synchronized (SingleTon.class) {
if (singleTon == null) {
singleTon = new SingleTon();
}
}
}
return singleTon;
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(SingleTon.getInstance().hashCode());
}
}
在上述代码中,singleTon = new SingleTon() 在内存中的执行顺序为
1,分配对象的内存空间 : memory = allocate();
2,初始化对象:ctorInstance(memory);
3,指向分配地址:singleTon = memory 多线程在执行的时候,2,3指令可能发生重排
而加上volatile 就可以避免指令重拍
3.4 使用静态内部类来实现懒汉单例模式
package SafeSingleTon4;
/**
* @Name: 解决多线程下懒汉单例模式的线程安全问题
* @Author:ZYJ
* @Date:2019-07-26-13:32
* @Description: 使用静态内部类来实现单例模式
*/
public class SingleTon {
private static class SingletonHandler{
private static SingleTon singleTon = new SingleTon();
}
private SingleTon(){}
public static SingleTon getInstance(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return SingletonHandler.singleTon;
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread1 = new Thread(myThread);
Thread thread2 = new Thread(myThread);
Thread thread3 = new Thread(myThread);
thread1.start();
thread2.start();
thread3.start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(SingleTon.getInstance().hashCode());
}
}
3.5 静态代码块实现单例模式
package SafeSingleTon5;
/**
* @Name: 解决多线程下懒汉单例模式的线程安全问题
* @Author:ZYJ
* @Date:2019-07-26-13:52
* @Description: 第五种 static 代码块来实现单例模式
*/
public class SingleTon {
private static SingleTon singleTon;
private SingleTon(){}
/**
* 静态代码块
*/
static {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleTon = new SingleTon();
}
public static SingleTon getInstance(){
return singleTon;
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread1 = new Thread(myThread);
Thread thread2 = new Thread(myThread);
Thread thread3 = new Thread(myThread);
thread1.start();
thread2.start();
thread3.start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(SingleTon.getInstance().hashCode());
}
}