单例编程java_单例 ------ JAVA实现

单例:只能实例化一个对象,使用场景比如打印机。

最推荐的是采用饿汉式;双重校验锁用到了大量的语法,不能保证这些语法在所用场合一定没问题,所以不是很推荐;总之简单的才是最好的,就饿汉式!!!

C++ 创建变量可以通过 类名 对象名,但是 JAVA 不行

C++ new 出来的对象需要手动回收,但是 JAVA 可以自动回收

C++ new 出来的对象需要用指针接收,但是 JAVA 对象变量就可以接收,或者说 JAVA 的对象变量就是指针

1、(懒汉,线程安全 ------ 不推荐)

public classSingleton {private staticSingleton instance;privateSingleton (){}public static synchronizedSingleton getInstance() {if (instance == null) {

instance= newSingleton();

}returninstance;

}

}

说明:在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的

2、(饿汉 ------ 推荐)

public classSingleton {private static Singleton instance = newSingleton();privateSingleton (){}public staticSingleton getInstance() {returninstance;

}

}

说明:instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载。所以,会出现在使用对象前就创建好,占用了资源

3、(静态内部类 ------ 推荐)

public classSingleton {private static classSingletonHolder {private static final Singleton INSTANCE = newSingleton();

}privateSingleton (){}public static finalSingleton getInstance() {returnSingletonHolder.INSTANCE;

}

}

说明:第三种和第二种的区别在于,第三种类被装载也不会实例化对象,一定要调用 getInstance() 才会实例化,而且只实例化一次。

4、(双重校验锁 ------ 一般推荐)

public classSingleton {private volatile staticSingleton uniqueInstance;privateSingleton (){}public staticSingleton getSingleton() {if (uniqueInstance == null) { // #1synchronized (Singleton.class) { // #2if (uniqueInstance == null) { // #3

uniqueInstance= newSingleton(); // #4

}

}

}returnsingleton;

}

}

说明:和第一种方式比,这种方式只会第一次调用创建对象时使用同步锁

对象singleton 用 volatile 修饰,原因举例如下:

1. thread2进入#1, 假设这时子线程的uniqueInstance为空(java内存模型会从主线程拷贝一份uniqueInstance=null到子线程thread2),然后thread2让出CPU资源给thread3

2. thread3进入#1, 这时子线程的uniqueInstance还是为空(java内存模型会从主线程拷贝一份uniqueInstance=null到子线程thread3),然后thread3让出CPU资源给thread2

3. thread2会依次执行#2,#3,#4,最终在thread2里面实例化了uniqueInstance(由于是volatile修饰的变量,会马上同步到主线程的变量去),thread2执行完毕让出CPU资源给thread3

4. thread3接着#1跑下去,跑到#3的时候,会又一次从主线程拷贝一份uniqueInstance!=null回来,所以thread3就不会实例化对象

上面解释的是 volatile 保证变量可见性的体现,使每个线程得到的变量来源都是唯一的;同时 volatile 还能保证变量的有序性," new Singleton "的执行其实是分三步走,第一:分配内存,第二:执行构造函数,第三:赋值,准确的说应该是引用;如果没有 volatile 修饰,在保证结果的情况下(单线程,或者是本线程中)编译器可能会打乱执行顺序,如果有另外一个线程在执行 #1 判断变量不为nll,但是此变量没有执行构造函数,获取的对象是不完整的,程序就可能发生错误。java的volatile顺序性方面比C++强,能保证此顺序执行。

volatile 防止编译器把两个 if 优化成一个 if 。

疑问:

1、如果赋值不是原子操作,会不会出现,没完全赋值完成就被另外一个线程使用了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值