JAVA中正确使用的单例模式常用写法(6种写法)
星期六, 2017-11-04 | Author: Lee | JAVA-and-J2EE | 2,969 views
单例模式 老生常谈的一种模式,也是实际项目代码中比较常用的一种模式了.
文中做了6种方法,实际用的最多的也就 4.5两种,其实6是最完美的但是感觉别捏呢,不像个类,用的也并不多.
1.懒汉式,线程不安全
public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } |
2.懒汉式,线程安全 (BUT.效率差)
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } |
3.双重检验锁
public static Singleton getSingleton() { if (instance == null) { //Single Checked synchronized (Singleton.class) { if (instance == null) { //Double Checked instance = new Singleton(); //非原子操作 } } } return instance ; } |
问题:new Singleton非原子操作
1.给 instance 分配内存
2.调用Singleton 的构造函数来初始化成员变量
3.将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)
解决办法 JDK1.5以前的版本会有问题, JDK1.5后用volatile关键字(立即可见及禁止指令重排序优化)来修饰变量.通过DCL(double checked locking)实现同步
public class Singleton { private volatile static Singleton instance; //这里为 volatile private Singleton (){} public static Singleton getSingleton() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } |
上面三种的写法基本上在项目中没有什么实用价值,下面的三种才是比较常用的
4.饿汉式 static final field,(无参数推荐使用)
public class Singleton{ //类加载时就初始化 private static final Singleton instance = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return instance; } } |
缺点:依赖参数或者配置文件无法在类初始的时候进行;
如果无参数或者要加载配置文件可以直接使用比较方便
5.静态内部类 static nested class (通用 推荐使用)
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } } |
6.枚举 Enum (可以实现接口的方式使用,感觉不像类文件,使用也不多)
public enum Singleton { INSTANCE { @Override protected void read() { System.out.println("read"); } @Override protected void write() { System.out.println("write"); } }; protected abstract void read(); protected abstract void write(); } |
注:优点:
1.线程安全
默认枚举实例的创建是线程安全的.(创建枚举类的单例在JVM层面也是能保证线程安全的),所以不需要担心线程安全的问题
2.不会因为序列化而产生新实例,枚举单例 对序列化有保证
以往的单例实现了序列化接口,那么就再也不能保持单例的状态了.因为readObject()方法一直返回一个新的对象.使用radResolve()来避免此情况发生.
//readResolve to prevent another instance of Singleton private Object readResolve(){ return INSTANCE; } |
3.防止反射攻击
采用反射来创建实例时.可通过AccessibleObject.setAccessible(),通过反射机制来调用私有构造器.那么枚举可以防止这种创建第二个实例的情况发生.
缺点:不像在使用一个CLASS的方式,使用起来感觉别捏,实际看到用的也并不多,也许以后会有很多人用(谁知道呢…)
文章作者: Lee
本文地址: https://www.pomelolee.com/1772.html
除非注明,Pomelo Lee文章均为原创,转载请以链接形式标明本文地址
No comments yet.
Leave a comment
Search
相关文章
热门文章
最新文章
文章分类
- ajax (10)
- algorithm-learn (3)
- Android (6)
- as (3)
- computer (85)
- Database (30)
- disucz (4)
- enterprise (1)
- erlang (2)
- flash (5)
- golang (3)
- html5 (18)
- ios (4)
- JAVA-and-J2EE (186)
- linux (143)
- mac (10)
- movie-music (11)
- pagemaker (36)
- php (50)
- spring-boot (2)
- Synology群晖 (2)
- Uncategorized (6)
- unity (1)
- webgame (15)
- wordpress (33)
- work-other (2)
- 低代码 (1)
- 体味生活 (40)
- 前端 (21)
- 大数据 (8)
- 游戏开发 (9)
- 爱上海 (19)
- 读书 (4)
- 软件 (3)