JAVA中的单例模式分析(doublecheck和枚举实现)
文章目录
在java中,单例模式的实现方法有如下几种:
1.饿汉模式
所为饿汉模式,即一开始就创建一个静态的对象,之后该对象一直存在。这种模式不会有线程安全问题。
package com.dhb.builder.singleton;
public class Singleton1 {
private static Singleton1 instance = new Singleton1();
private Singleton1() {
}
public static Singleton1 getInstance() {
return Singleton1.instance;
}
}
2.懒汉模式
对于饿汉模式,优点在于实现简单。但是存在一个问题就是 instance 只要 Singleton1被加载就会被创建到static所在的静态方法区。如实现数据库连接池等情况,用这种方法一上来就要创建数据库的连接资源,实际系统中暂不使用。这就造成了资源的浪费。因此,对于这种情况,出现了与之对应的懒汉模式。 即一开始并不创建对象,待需要使用时再new。
package com.dhb.builder.singleton;
public class SingletonDemo1 {
private static SingletonDemo1 instance = null;
private SingletonDemo1() {
}
/**
* 存在线程安全问题
* @return
*/
public static SingletonDemo1 getInstance() {
if(instance == null) {
instance = new SingletonDemo1();
}
return instance;
}
}
这是大家想到的最常用的懒汉模式的写法。但是问题来了,上述模式在多线程的情况下是线程不安全的!也就是说,如果有两个线程,同时getInstance(),同时都会判断instance的值为null。这种情况下会创建多个实例。 为了解决上述问题,我们引入了锁:
package com.dhb.builder.singleton;
public class SingletonDemo2 {
private static SingletonDemo2 instance = null;
private SingletonDemo2() {
}
/**
* 增加同步机制,解决线程安全
* @return
*/
public static synchronized SingletonDemo2 getInstance() {
if(instance == null) {
instance = new SingletonDemo2();
}
return instance;
}
}
上面这种做法,确实解决了线程安全问题,但是带来了一个更加不好的问题,那就是每一次请求都会加锁!这样会严重影响性能。更好的做法是采用双重检查机制:
package com.dhb.builder.singleton;
public class SingletonDemo3 {
private static SingletonDemo3 instance = null;
private SingletonDemo3() {
}
/**
* 增加双重检查机制,解决synchronized效率问题
* @return
*/
public static SingletonDemo3 getInstance() {
if(instance == null)
synchronized (SingletonDemo3.class) {
if (instance == null) {
instance = new SingletonDemo3();
}
}
return instance;
}
}
上述单例实际仍然存在问题,那就是类初始化仍然需要时间,如果同时又两个线程同时进入getInstance方法,第一个线程锁定之后,第二个线程判断不为空,则直接使用instalce,如果此时第一个线程对SingletonDemo3对象还没实例化完成,如该对象内部存在一个耗时的引用,如果是一个数据库连接,则会导致第二个线程使用的对象不完整。出现空指针。因此更好的写法是加上volatile。以保证happen-before原则。
package com.dhb.builder.singleton;
public class SingletonDemo4 {
private volatile static SingletonDemo4 instance = null;
private SingletonDemo4() {
}
/**
* 增加双重检查机制,解决synchronized效率问题
* @return
*/
public static SingletonDemo4 getInstance() {
if(instance == null)
synchronized (SingletonDemo4.class) {
if (instance == null) {
instance = new SingletonDemo4();
}
}
return instance;
}
}
这样单例模式才完全解决。上述方法比较冗繁,有没有更好的解决办法呢,有幸阅读过《effective java》这本书对于单例有更好的解决办法。
3.更好的解决办法
第一种方法,利用静态内部类:
package com.dhb.builder.singleton;
import java.util.stream.IntStream;
public class SingletonHolder {
private SingletonHolder() {
}
private static class InstanceHolder{
private final static SingletonHolder INSTANCE = new SingletonHolder();
}
public static SingletonHolder getInstance() {
return InstanceHolder.INSTANCE;
}
public static void main(String[] args) {
IntStream.rangeClosed(1,100).forEach(i -> new Thread(String.valueOf(i)){
@Override
public void run() {
System.out.println(SingletonHolder.getInstance());
}
}.start());
}
}
上述方法执行结果:
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
可以看出SingletonHolder类只实例化了一次。这种方法很巧妙地利用一个内部类,很简单的代码即实现了单例,而且是线程安全。
方式二:《effective java》中还有一种更简单的写法,那就是枚举。也是《effective java》作者最为推崇的方法。
package com.dhb.builder.singleton;
import java.util.stream.IntStream;
public class SingletonEnum {
private SingletonEnum() {
}
private enum Singleton {
INSTANCE;
private final SingletonEnum instance;
Singleton() {
instance = new SingletonEnum();
}
public SingletonEnum getInstance() {
return instance;
}
}
public static SingletonEnum getInstance() {
return Singleton.INSTANCE.getInstance();
}
public static void main(String[] args) {
IntStream.rangeClosed(1,100).forEach(i -> new Thread(String.valueOf(i)){
@Override
public void run() {
System.out.println(SingletonEnum.getInstance());
}
}.start());
}
}
上述方法执行结果:
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
在java中,枚举天然实现了单例模式。其构造方法只会实例化一次。
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- NFS+NIS+Autofs 实现用户的集中化管理
- [docker]Tomcat安装及配置访问权限
- Nginx+Keepalived 保障HA高可用
- Hash一致性闭环算法 - ( 适用于Redis扩容、Nginx多级缓存 等等 )
- MySQl 事务测试
- 百万数据,SQL数据分流查询
- Linux 安装Apr - 提高Tomcat 的可伸缩性和性能
- Linux下MySQL的彻底卸载
- Excel生成导入SQL语句,快速创建批量 insert/update/delete
- MySQL 执行计划详解
- MySQL 5.7详细安装步骤
- win10必备效率预览神器-Quick look
- 史上最全-Nginx和Tengine安装部署
- Jenkins+Ansible 实现自动化运维 DevOps
- FFmpeg 内容介绍 音视频解码和播放