饿汉模式
package 实现;
/**
* 懒汉模式:1 这样的优点在于 没有多线程的麻烦 2 能够保证每次实现的都是唯一的单例
*
* 缺点在于:
* 每次不论需不需要单例模式都需要去创建一个实例,会造成Java虚拟机内存的浪费。
* @author jhc
*
*/
public class Singleton {
private Singleton(){
}
private static Singleton s1 = new Singleton();
public static Singleton getInstance(){
return s1;
}
}
懒汉模式
package 实现;
/**
* 懒汉模式
* 优点在于不会造成内存的浪费。
* 但是缺点在于:当多个线程创建的时候会造成创建多个实例。
* 在#1这一步的时候,当有多个线程存在的时候,会创建两个不同的实例。
* @author jhc
*
*/
public class Singleton1 {
private Singleton1(){
}
private static Singleton1 s1 = null;
public static Singleton1 getInstance(){
if(s1 == null){//#1
s1 = new Singleton1();
}
return s1;
}
}
同步异常的做法
package 实现;
/**
* 使用synchronize关键字创建对象
* 这个方法是对Singleton1的补充。但是这样做的缺点在于:每次只有一个线程能够得到实例
* 不能实现高的并发。
* @author jhc
*
*/
public class Singleton2 {
private Singleton2(){
}
private static Singleton2 s1 = null;
public static Singleton2 getInstance(){
synchronized(Singleton2.class){
if(s1 == null){
s1 = new Singleton2();
}
return s1;
}
}
}
双重检查的错误方式
package 实现;
/**
* 使用双重检查
* 首先一开始的检查并没有使用锁:这样的目的是为了提高并发性。
* 在后续使用的时候,对Singleton3加锁,这样能够保证创建的唯一性。
* 但是这里的一个问题在于:如果两个线程A,B都通过了第一次的检查,在AB依次执行到
* #2的时候,这个时候都会创建一个新的Singleton3这样便没有办法保证创建的唯一性。
* 所以需要的条件是:#1处加上volatile
*
* @author jhc
*
*/
public class Singleton3 {
private Singleton3(){
}
private static Singleton3 s3 = null;//#1
public static Singleton3 getInstance(){
if(s3 == null){
synchronized(Singleton3.class){
if(s3 == null){//#2
s3 = new Singleton3();
}
}
}
return s3;
}
}
双重检查的正确方式
package 实现;
/**
* volatile关键字的两个作用:1是禁止指令重排序,这个使用其作为标志位判断一些内容加载完成后,因为在Java会在编译器优化
* 2 一个线程修改的内容,能够对所有线程可见
* @author jhc
*
*/
public class Singleton4 {
private Singleton4(){
}
private static volatile Singleton4 s1 = null;
public static Singleton4 getInstance(){
if(s1 == null){
synchronized(Singleton4.class){
if(s1 == null){
s1 = new Singleton4();
}
}
}
return s1;
}
}