关于Java单例
线程安全、延迟加载、序列化反序列化反射安全
参考资料:
http://blog.csdn.net/haoel/article/details/4028232
public class SingletonTest implements Runnable {
static SingletonClass1 instance = null;
public static void main(String args[]) {
// SingletonClass0 instance = new SingletonClass0();// The constructor
// SingletonClass0() is not visible
for (int i = 0; i < 15; i++) {
new Thread(new SingletonTest()).start();
}
// System.out.println(instance.getClass());
}
@Override
public void run() {
// TODO Auto-generated method stub
SingletonClass6.getInstance();
// System.out.println(SingletonClass7.instance.hashCode());
}
}
class SingletonClass0 {
private static SingletonClass0 instance = null;
private SingletonClass0() {
System.out.println("constructor run");
}
public static SingletonClass0 getInstance() {// wrong 单线才单线程下没问题,多线程下仍会有多个实例
if (instance == null) {
instance = new SingletonClass0();
System.out.println(instance.hashCode());
}
return instance;
}
}
class SingletonClass1 {
private static SingletonClass1 instance = null;
private SingletonClass1() {
System.out.println("constructor run");
}
public static SingletonClass1 getInstance() {// wrong 多线程下仍会有多个实例,与上一种
// 区别在于过个线程的new过程被同步了
if (instance == null) {
synchronized (SingletonClass1.class) {
instance = new SingletonClass1();
System.out.println(instance.hashCode());
}
}
return instance;
}
}
class SingletonClass2 {
private static SingletonClass2 instance = null;
private SingletonClass2() {
System.out.println("constructor run");
}
public static SingletonClass2 getInstance() {
synchronized (SingletonClass2.class) {// right。只会有一个线程new实例,但阻碍了后续线程读实例
if (instance == null) {
instance = new SingletonClass2();
System.out.println(instance.hashCode());
}
}
return instance;
}
}
class SingletonClass3 {
private static SingletonClass3 instance = null;
private SingletonClass3() {
System.out.println("constructor run");
}
public static SingletonClass3 getInstance() {
synchronized (SingletonClass3.class) {// right
// 只会有一个线程new实例,不会影响后续线程读实例,但instance=new
// SingletonClass3()在JVM内不是原子操作,内部的几个步骤可能乱序,从而出错
if (instance == null) {
synchronized (SingletonClass1.class) {
if (instance == null) {
instance = new SingletonClass3();
System.out.println(instance.hashCode());
}
}
}
}
return instance;
}
}
class SingletonClass4 {
private static volatile SingletonClass4 instance = null;// right
// 只有一个实例,volatite保证了在JVM内部new
// instance的几个操作禁止指令重排序优化
private SingletonClass4() {
System.out.println("constructor run");
}
public static SingletonClass4 getInstance() {
synchronized (SingletonClass4.class) {
if (instance == null) {
synchronized (SingletonClass1.class) {
if (instance == null) {
instance = new SingletonClass4();
System.out.println(instance.hashCode());
}
}
}
}
return instance;
}
}
class SingletonClass5 {// right,但是由类加载器在类加载时创建实例,我们无法控制实例创建的时机以干一些事(比如某个配置文件,或是某个被其它类创建的资源)
public volatile static SingletonClass5 instance = new SingletonClass5();
private SingletonClass5() {
System.out.println("constructor run");
};
public static SingletonClass5 getInstance() {
System.out.println(instance.hashCode());
return instance;
}
}
class SingletonClass6 {// 仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了
// getInstance()
// 之外没有办法访问它,因此它只有在getInstance()被调用时才会真正创建;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖
// JDK 版本
private static class SingletonHolder {
private static final SingletonClass6 INSTANCE = new SingletonClass6();
}
private SingletonClass6() {
System.out.println("constructor run");
}
public static final SingletonClass6 getInstance() {
System.out.println(SingletonHolder.INSTANCE.hashCode());
return SingletonHolder.INSTANCE;
}
}
class SingletonClass7 {
public SingletonClass7() {
System.out.println("constructor run");
}
}
enum SingletonClass {
instance;
}
http://mp.weixin.qq.com/s/q7-GWt87S7uJ9NY-s1z4SA
需要参数:懒汉式1。(线程安全改进3、需要才new改进4)
不需要参数:饿汉式2。(本身线程安全、需要才new改进5)
上述几种实现方式都存在反射、序列化、反序列化使得单例被破坏的问题,可能成为漏洞被利用,虽可采用手段加以解决(解决方式见下)但不简单高效(若单例类维持了其他对象的状态时还需要使他们成为transient的对象,此时就更复杂了) -> 使用枚举6。
反序列化时单例被破坏的解决方法:readResolve
public class Singleton implements java.io.Serializable {
public static Singleton INSTANCE = new Singleton();
protected Singleton() {
}
//反序列时直接返回当前INSTANCE
private Object readResolve() {
return INSTANCE;
}
}
反射时单例被破坏的解决方法:修改构造器,让它在创建第二个实例的时候抛异常
public static Singleton INSTANCE = new Singleton();
private static volatile boolean flag = true;
private Singleton(){
if(flag){
flag = false;
}else{
throw new RuntimeException("The instance already exists !");
}
}

1、

2、

3、

4、双_锁检测(DCL),JDK1.5之前volatile不能完全避免指令重排序优化,直到1.5才修复。因此JDK1.5之前Java中无法安全地使用DCL来实现单例模式。

5、

6、

枚举类实际上就是一个继承Enum类的类。
使用枚举单例的写法,我们完全不用考虑序列化和反射的问题。枚举序列化是由jvm保证的,每一个枚举类型和定义的枚举变量在JVM中都是唯一的,在枚举类型的序列化和反序列化上,Java做了特殊的规定:在序列化时Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的并禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法,从而保证了枚举实例的唯一性。
在单例中,枚举也不是万能的。在android开发中,内存优化是个大块头,而使用枚举时占用的内存常常是静态变量的两倍还多,因此android官方在内存优化方面给出的建议是尽量避免在android中使用enum。
更多关于枚举可以见:Java小计-枚举
关于Java单例的更多相关文章
- java单例的几种实现方法
java单例的几种实现方法: 方式1: public class Something { private Something() {} private static class LazyHolder ...
- java单例类/
java单例类 一个类只能创建一个实例,那么这个类就是一个单例类 可以重写toString方法 输出想要输出的内容 可以重写equcal来比较想要比较的内容是否相等 对于final修饰的成员变量 一 ...
- java单例-积木系列
一步步知识点归纳吧,把以前似懂非懂,了解表面,知道点不知道面的知识归一下档. 懒汉式单例: 私有化构造函数,阻止外界实例话对象,调用getInstance静态方法,判断是否已经实例化. 为什么是懒 ...
- Java单例类的简单实现
对于java新手来说,单例类给我的印象挺深,之前一道web后台笔试题就是写单例类.*.*可惜当时不了解. 在大部分时候,我们将类的构造器定义成public访问权限,允许任何类自由创建该类的对象.但在某 ...
- 【Java学习笔记之三十】详解Java单例(Singleton)模式
概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...
- 转:java单例设计模式
本文转自:http://www.cnblogs.com/yinxiaoqiexuxing/p/5605338.html 单例设计模式 Singleton是一种创建型模式,指某个类采用Singleton ...
- 熟悉的味道——从Java单例写到C++单例
设计模式中,单例模式是常见的一种.单例模式需要满足以下两个条件: 保证一个类只能创建一个示例: 提供对该实例的全局访问点. 关于单例最经典的问题就是DCL(Double-Checked Lock),今 ...
- java单例五种实现模式梳理
java单例五种实现模式 饿汉式(线程安全,调用效率高,但是不能延时加载) 一上来就把单例对象创建出来了,要用的时候直接返回即可,这种可以说是单例模式中最简单的一种实现方式.但是问题也比较明显.单例在 ...
- JAVA单例实现方式(常用)
JAVA单例实现方式(常用) public class Singleton { // Q1:为什么要使用volatile关键字? private volatile static Singleton u ...
- Java单例设计模式的实现
1. 单例设计模式的定义 单例设计模式确保类只有一个实例对象,类本身负责创建自己的对象并向整个系统提供这个实例.在访问这个对象的时候,访问者可以直接获取到这个唯一对象而不必由访问者进行实例化. 单例设 ...
随机推荐
- [Unity3D] 和Unity3D的一些事
Before 一开始知道Unty3D引擎,貌似还是大一或者大二,那时Unity3D还是收费软件,不像现在Personal Version免费. XNA|MonoGame 再后来,在Coursera上发 ...
- Linux非root用户安装jdk和tomcat
转载自:http://blog.csdn.net/wuyigong111/article/details/17410661,进行部分修改 创建一个用户 sgmm,并在其用户目录里面安装 jdk和tom ...
- 信息安全系统设计基础exp_3
详情请看搭档20135322郑伟博客,传送门如下:http://www.cnblogs.com/zhengwei0712/p/4996017.html
- jsoup抓取借书记录
package tushuguan; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; ...
- ios上uiwebview的一些实用技巧
前几个星期接到公司一个项目,要用webview在客户端上播视频,作为一个前端实习生,这种需求真是蛋疼……一不知webview是何方神圣,二不知咋调试…… 下面就是蛋疼的开始: 寻找调试工具:好,非w ...
- php 读取文件的几种方法
文件操作的三个步骤,打开,操作,关闭.$fopen=fopen(路径,方式),fwrite($fopen,写入的字符串);fclose($fopen). 其中打开方式有如下几种方式: 模式 描述 r ...
- VMware打卡虚拟机提示“此虚拟机可能已被复制或移动”
使用VMware打开虚拟机时出现下图的页面,我来解释一下这三个选项按钮的区别与作用. "我已移动虚拟机" //表示打开后的虚拟的网卡的mac地址不变,如果复制本地的,同时开 ...
- QQ提醒的功能
原文出处:http://www.cnblogs.com/xiexingen/archive/2013/04/09/3009921.html http://qzs.qq.com/snsapp/app/b ...
- linux用户管理命令
关键字 useradd passwd who w uptime 1.useradd添加用户命令 useradd 用户名 passwd 用户名 (设置密码) 2.userdel 删除用户 userdel ...
- C#配置文件管理
最近在做项目的过程中用到配置文件,本文简要说明本人在项目过程中是如何使用配置文件的,目的是加深自己对配置文件管理的理解,以便在下次使用时能做到轻松自如. 配置文件,顾名思义,是用户在使用系统或者软件时 ...