单例模式的几种写法和优缺点
-
饿汉式 静态常量
/**
* 饿汉式 静态常量
* <p>在类加载的时候,就已经初始化,无论之后用没用到。这样写法简单,所以
* 优点:<p> 写法简单,避免多线程的同步问题
* <p>
* 劣:<p> 没有达到懒加载的效果 消耗过多的内存
*/
class Singleton {
/**
* 类的内部创建 对象 static final
*/
private static final Singleton instance = new Singleton ();
/**
* 构造器私有化 防止外部用 构造器
*/
private Singleton () {
}
public static Singleton getInstance() {
return instance;
}
}
2:懒汉式
1:线程不安全
/**
* 懒汉式 线程不安全
*
* <p>
* 优:<p> 与饿汉式单例不同的是,懒汉式单例是要在用到的时候才实例化 实现了懒加载 也不会消耗内存
* <p>
* 劣:<p> 但是在多线程的情况下会出现线程不安全情况,可能会出现 回去的实例不是一个对象
*/
class Singleton {
private Singleton () {
}
private static Singleton instance;
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton ();
}
return instance;
}
}
2:懒汉式 同步方法
/**
* 懒汉式 同步方法
* <p>
* 优:<p> 添加了synchronized 解决了线程安全的问题
* <p>
* 劣:<p> 多个线程在获取实例时,只能有一个线程能获取到锁,调用getInstance()方法,其他线程会被阻塞在外边。效率太低
*/
class Singleton {
private Singleton () {
}
private static Singleton instance;
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton ();
}
return instance;
}
}
3:懒汉式 同步代码块
/**
* 懒汉式 同步代码块
* <p>
* 为了不让线程在方法外等待,而是实例不为空就直接返回,可以这么改写getInstance()方法:
*
* <p> 不推荐 :解决不了线程安全问题
*/
class Singleton {
private Singleton () {
}
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton .class) {
instance = new Singleton();
}
}
return instance;
}
}
3 :双重检查机制
但是发现得到的并不是同一个对象,这是因为当一个线程进入synchronize中的代码块时,还没来得及实例化,另一个线程判断(singleton==null)进入if语句,但是没获取到锁,在外面等待,当前一个线程实例化完后,释放了锁,后一个线程拿到锁继续执行,又实例化了一个对象。所以这样写不是线程安全的。
优点
线程安全
解决了线程安全问题
懒加载
不会造成内存的浪费
效率高
效率相对较高
缺点:
可能会出现初始化为空的情况出现
/**
* 双重检查机制
* <p> 推荐使用
* <p>
* 优:<p>
* 线程安全
* 解决了线程安全问题
* 懒加载
* 不会造成内存的浪费
* 效率高
* 效率相对较高
*/
class Singleton {
private Singleton() {
}
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
但是 在jvm中,new操作并不是原子性的,一个new语句包括了:
1.给instance分配空间
2.调用 Singleton 的构造函数来初始化
3.将instance对象指向分配的内存空间
在JVM中的及时编译存在指令重排序的优化,也就是说,上述执行顺序不能保证,如果第一个线程在实例化对象时,2执行在3前边,当线程执行完2,singleton就已经非空了,如果其他线程在第一个线程为执行完3之前,调用getInstance()方法将获取到一个不完整的对象(未初始化),使用则会报错。如:排序
1.给instance分配空间
2.将instance对象指向分配的内存空间
3.调用 Singleton 的构造函数来初始化
所以需要volatile关键字修饰singeton,禁止JVM进行指令重排序。(这是最正确的写法,要记住)
/**
* 双层检查机制
*
*/
class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
*********************************************************************************************************************************************
JAVA 式 kotlin 单例 能使用在kotlin 代码中
/**
* 懒加载
*/
class Singleton {
companion object {
private var instance: Singleton? = null
fun getInstance(): Singleton {
if (instance == null) {
instance = Singleton ()
}
return instance!!
}
}
}
线程安全
/**
* 懒加载 线程安全
*/
class Singleton {
companion object {
private var instance: Singleton? = null
@Synchronized
fun getInstance(): Singleton{
if (instance == null) {
instance = Singleton()
}
return instance!!
}
}
}
双重锁
/**
* 懒加载 双重 锁
*/
class Singleton {
companion object {
@Volatile
private var instance: Singleton? = null
fun getInstance(): Singleton{
synchronized(this) {
if (instance == null) {
instance = Singleton()
}
}
return instance!!
}
}
}
kotlin 代码 单例
/**
* 懒汉式 kotlin 模板的 推荐 by lazy
*/
class Singleton private constructor() {
companion object {
// 默认是加锁的
val getInstance: Singleton by lazy {
DoubleCheckSin()
}
}
}
与上面的区别是 可在 by lazy 中设置模式
class Singleton private constructor() {
companion object {
//LazyThreadSafetyMode.NONE 没有锁 SYNCHRONIZED 加锁 不写NONE 默认情况下是加锁的模式
val instance by lazy(LazyThreadSafetyMode.NONE) {
Singleton()
}
}
}
静态内部类
/**
* 静态内部类
*/
class InnerSin constructor() {
companion object {
val instance = Holder.holder
}
private object Holder {
val holder = InnerSin()
}
}