这里写自定义目录标题
饿汉式
public class Singleton2 {
private static Singleton2 uniqueInstance = new Singleton2();
private Singleton2() {
}
public static Singleton2 getInstance() {
return uniqueInstance;
}
}
懒汉式
/**
* 单例模式:确保一个类只有一个实例,并提供一个全局访问点
* 管理共享的资源如:线程池,注册表设置,数据库连接池
* 如果使用多个类加载器,会导致单例失效而产生多个实例
* 解决方案:自行指定类加载器,并指向同一个类加载器
* 全局变量缺点
* 不能确保只有一个实例
* 变量过多导致命名空间污染
* @author Administrator
*
*/
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getInstance() {
if (uniqueInstance == null)
uniqueInstance = new Singleton();
return uniqueInstance;
}
}
懒汉式引发的并发问题解决方案
方法添加同步
/**
* 多线程解决方案1
* 缺点:只有第一次执行方法时才需要同步,设置好uniqueInstance变量,不再需要sync了,之后每次调用都浪费资源
*
* @author Administrator
*
*/
public class Singleton1 {
private static Singleton1 uniqueInstance;
private Singleton1() {
}
public static synchronized Singleton1 getInstance() {
if (uniqueInstance == null)
uniqueInstance = new Singleton1();
return uniqueInstance;
}
}
使用独占锁
这里引入一个双重检查加锁的概念
为什么需要判断二次对象是否为null?
- 当线程A和线程B抢占锁,假设A抢成功,B则会阻塞.A执行结束后,这个时候单例对象已经创建,B抢到锁,如果继续执行单例对象初始化就会重复创建,这个时候很有必要再次检测此单例对象是否为null.
为什么对象需要声明volatile ?
new Singleton3()并不是一个原子操作
// 创建 Cache 对象实例,分配内存
0: new #5 // class com/query/Cache
// 复制栈顶地址,并再将其压入栈顶
3: dup
// 调用构造器方法,初始化 Cache 对象
4: invokespecial #6 // Method "<init>":()V
// 存入局部方法变量表
7: astore_1
假设当执行到第三步,另一个线程执行getInstance()获取对象,获取的会是一个尚未初始化完成的对象.
/**
* 多线程解决方案3:双重检查加锁
*
* @author Administrator
*
*/
public class Singleton3 {
// volatile:线程在每次使用变量的时候,都会读取变量修改后的最的值。
private volatile static Singleton3 uniqueInstance;
private Singleton3() {
}
public static Singleton3 getInstance() {
//检查实例,如果不存在就进入同步去
if (uniqueInstance == null) {
//只有第一次才彻底执行这里的代码
synchronized (Singleton3.class) {
//进入区块后,在检查一次,如果依然是null才创建实例
if (uniqueInstance == null)
uniqueInstance = new Singleton3();
}
}
return uniqueInstance;
}
}
使用静态内部类
首先static的在类加载阶段只会加载一次,这就保证了之后每次使用单例对象不需要重复加载的问题.
第二当执行static的时候会执行clinit()方法给静态对象赋值,当多个线程抢占的时候,会有加锁操作,只会有一个线程成功执行clinit()方法,且执行成功后,后续的线程不会再执行clinit()方法,这就保证了clinit()只会执行一次,也就是说static修饰的对象只会new一次.
也就解决了上述单例对象创建需要每次判断是否为null以及重复加锁的问题了.
/**
当第一次访问类中的静态字段时,会触发类加载,并且
同一个类只加载一次。静态内部类也是如此,类加载过程由类加载器负责加锁,从而保证线程
安全。
* @author Administrator
*
*/
public class Singleton4 {
private static class SingletonHolder {
//静态字段
public static Singleton4 instance = new Singleton4();
}
private Singleton4() {
}
public static Singleton4 newinstance () {
// 访问静态内部类中的静态字段
return SingletonHolder.instance;
}
}