设计模式之(单例模式)
java的单例模式是一种常见的设计模式,单例模式的写法有好几种,主要分为懒汉式单例,饿汉式单例还有登记式单例
下面我们要介绍的就是多线程下的单例模式,多线程,我们以继承Thread和实现Runnable接口为主
首先我们以饿汉模式为例
package lock;
public class EhanSingleton {
/*饿汉加载模式/立即加载模式*/
//初始化构造函数
private EhanSingleton(){
}
private static EhanSingleton ehan = new EhanSingleton();
public static EhanSingleton getInstance(){
try {
Thread.sleep(3000); //方便多线程测试
} catch (InterruptedException e) {
e.printStackTrace();
}
return ehan ;
}
}
然后我们创建多个线程来测试一下
package lock;
public class MyThread extends Thread{
@Override
public void run() {
System.out.println(EhanSingleton.getInstance());
}
public static void main(String[] args) {
MyThread m1 = new MyThread();
MyThread m12 = new MyThread();
MyThread m13 = new MyThread();
m1.start();
m12.start();
m13.start();
}
}
测试结果为:cn.spring.dxcDanli.Singleton@132965a8
cn.spring.dxcDanli.Singleton@132965a8
cn.spring.dxcDanli.Singleton@132965a8
显示为三个相同的内存地址,说明这个模式符合单例
第二种模式为懒汉模式
package lock;
public class LanHanSingleton {
/*懒汉模式/延迟加载*/
//私有化构造函数
private LanHanSingleton(){
}
private static LanHanSingleton lanHanSingleton ;
public static LanHanSingleton getInstance() {
if(lanHanSingleton == null){
try {
Thread.sleep(3000);
lanHanSingleton = new LanHanSingleton() ;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return lanHanSingleton ;
}
}
我在用多线程去测试一下是否每个对象的HashCode的值是保持一致的
package lock;
public class MyThread extends Thread{
@Override
public void run() {
System.out.println(LanHanSingleton.getInstance().hashCode());
}
public static void main(String[] args) {
MyThread m1 = new MyThread();
MyThread m12 = new MyThread();
MyThread m13 = new MyThread();
m1.start();
m12.start();
m13.start();
}
}
测试结果如下:
126720696
137014984
1638443495
测试的结果发现 这个已经不符合单例模式,他们并不是同一个对象了,而是几个不同的对象,所以这种懒汉模式在单线程中是符合单例模式的,不过在多线程环境中是不符合单例模式
想到这里,大家肯定会想到了synchrinized关键字,我们在来看看效果
package lock;
public class LanHanSingleton {
/*懒汉模式/延迟加载*/
//私有化构造函数
private LanHanSingleton(){
}
private static LanHanSingleton lanHanSingleton ;
public synchronized static LanHanSingleton getInstance() {
if(lanHanSingleton == null){
try {
Thread.sleep(3000);
lanHanSingleton = new LanHanSingleton() ;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return lanHanSingleton ;
}
}
测试结果如下
1638443495
1638443495
1638443495
大家发现这样的确可以解决多线程带来的不同对象所导致的问题,但是这个方法并不好,这种方法效率非常低下,一定要等到上一个线程释放锁以后才能获取对象
同步方法是对整个方法持有锁,这个对于效率来说实在太慢,大家还会想到用同步块,那么我们在试一试
package lock;
public class LanHanSingleton {
/*懒汉模式/延迟加载*/
//私有化构造函数
private LanHanSingleton(){
}
private static LanHanSingleton lanHanSingleton ;
public static LanHanSingleton getInstance() {
try {
synchronized (LanHanSingleton.class) {
if(lanHanSingleton == null){
Thread.sleep(3000);
lanHanSingleton = new LanHanSingleton() ;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return lanHanSingleton ;
}
}
其实这个效果和上个效果差不多,效率都是比较慢的,和同步方法synchronized一样是同步运行的
这里最好的方式就是DCL双检查锁机制,也就是同步代码块
package lock;
public class LanHanSingleton {
/*懒汉模式/延迟加载*/
//私有化构造函数
private LanHanSingleton(){
}
private static LanHanSingleton lanHanSingleton ;
public static LanHanSingleton getInstance() {
try {
if(lanHanSingleton == null){
synchronized (LanHanSingleton.class) {
if(lanHanSingleton == null){
lanHanSingleton = new LanHanSingleton() ;
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return lanHanSingleton ;
}
}