一 概念
单例模式:顾名思义就是只能实例化一个对象的设计模式。很多开发人员一听到单例模式,都会觉得很简单,对于这样的同学,我想问一下,你真的理解单例模式吗? 单例模式有多少种写法? 单例模式的DCL?DCL为啥需要需要用到valitile?等问题都理解么?作为一个程序员,咱话不多说,带着问题撸代码。
二 单例模式实战
1 饿汉模式
饿汉模式代码如下:
public class Singleton01 {
//恶汉模式需要提前实例化一个类对象
private static Singleton01 signleton01 = new Singleton01();
//私有化构造器
private Singleton01(){};
//提供一个方法去实例化对象
public static Singleton01 getInstance(){
return signleton01;
}
}
饿汉模式代码简单,但是有一个问题,就是在没有用到这个类的时候,jvm虚拟机加载类的时候也会实例化这个对象。
2 饱汉模式
饱汉模式代码如下:
public class Singleton02 {
//饿汉模式提前不会实例化对象,只有等到需要用的时候才会实例化
private static Singleton02 singleton02 = null;
//私有化构造器
private Singleton02(){};
//利用同步锁来实现线程安全
public static synchronized Singleton02 getInstance(){
if(singleton02 == null){
singleton02 = new Singleton02();
}
return singleton02;
}
}
饱汉模式的方法会有一个问题,就是同步锁的效率很低,会影响到效率。
3 饱汉模式DCL模式
饱汉DCL模式代码如下:
public class Singleton03 {
//饿汉模式提前不会实例化对象,只有等到需要用的时候才会实例化, 通过volatile关键字来实现禁止指令重排序
private static volatile Singleton03 singleton03 = null;
//私有化构造器
private Singleton03(){};
//利用同步代码块来实现线程安全,从而在同步方法的基础上提高了效率
public static Singleton03 getInstance(){
if(singleton03 == null){
synchronized(Singleton03.class){
if(singleton03 == null){
singleton03 = new Singleton03();
}
}
}
return singleton03;
}
}
饱汉模式的DCL下必须要加上volatile关键字。原因如下在new Singleton03();这段代码在执行的时候有3个阶段,第一步 申请一块内存空间,这个时候这个类的所有属性都是默认值。第二步 初始化类对象属性, 第三步 实例化对象。如果没有加volatile关键 字的话,那么jvm在编译class的时候会出现指令重排序的现象。导致第二步和第3步顺序调换(概率很小,但是存在)。这样会导致属性值丢失的情况。
4 枚举的方式实现单例模式
枚举的方式实现单例模式的代码如下:
public enum Singleton04 {
instance;
}
枚举的方式可以实现单例,还可以防止反序列化。
5 通过静态内部类的方式来实现单例模式
静态内部类的方式来实现单例模式的代码如下:
public class Singleton05 {
private static class Singleton05Hoder{
private static Singleton05 singleton05 = new Singleton05();
}
private static Singleton05 getInstatnce(){
return Singleton05.Singleton05Hoder.singleton05;
}
public static void main(String[] args){
for(int i=0; i<100; i++){
new Thread(new Runnable(){
@Override
public void run() {
System.out.println(Singleton05.getInstatnce().hashCode());
}
}).start();
}
}
}
6 通过容器的方式来实现单例模式
通过容器的方式来实现单例模式的代码如下:
import java.util.concurrent.ConcurrentHashMap;
public class Singleton06 {
private static ConcurrentHashMap<String, Singleton06> map = new ConcurrentHashMap<String, Singleton06>();
public static Singleton06 getInstance(String key){
map.putIfAbsent(key, new Singleton06());
return map.get(key);
}
public static void main(String[] args){
for(int i=0; i<100; i++){
new Thread(new Runnable(){
@Override
public void run() {
System.out.println(Singleton06.getInstance("Singleton06").hashCode());
}
}).start();
}
}
}
容器方式实现单例得需要使用ConcurrentHashMap来保证线程安全。