1)饿汉式
所谓懒汉式就是在第一次加载的时候就创建对象实例,而不管实际需不需要
public Class Singleleton{
private static Singleton singleleton= new Singleton();
public Singleleton{};
public Singleleton getSingleton() {
return singleleton;
}
}
2)懒汉式
这种方法可以实现延时加载,但是有一个致命弱点:线程不安全。如果有两条线程同时调用getSingleton()方法,就有很大可能导致重复创建对象
public Class Singleleton{
private static Singleton singleleton= null;
public Singleleton{};
public Singleleton getSingleton() {
if(singleleton==null) {
singleleton = new Singleleton();
}
return singleleton;
}
}
3)线程安全
虽然这种写法是可以正确运行的,但是其效率低下,还是无法实际应用。因为每次调用getSingleton()方法,都必须在synchronized这里进行排队,而真正遇到需要new的情况是非常少的。
public Class Singleleton{
private static volatile Singleton singleleton= null;
public Singleleton{};
public Singleleton getSingleton() {
syschronized(Singleton.class) {
if(singleton == null){
singleton = new Singleton();
}
}
return singleleton;
}
}
4)双重加锁
volatile这个关键字有两层语义。第一层可见性。可见性指的是在一个线程中对该变量的修改会马上由工作内存(Work Memory)写回主内存(Main Memory),所以会马上反应在其它线程的读取操作中。工作内存是线程独享的,主存是线程共享的。v第二层语义是禁止指令重排序优化。我们写的代码(尤其是多线程代码),由于编译器优化,在实际执行的时候可能与我们编写的顺序不同。编译器只保证程序执行结果与源代码相同,却不保证实际指令的顺序与源代码相同。这在单线程看起来没什么问题,然而一旦引入多线程,这种乱序就可能导致严重问题。volatile关键字就可以从语义上解决这个问题。
volatile由于被其所修饰的变量的值不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存来实现,从而确保多个线程能正确的处理该变量。该关键字会屏蔽掉虚拟机中的一些代码优化,所以其运行效率可能不是很高,所以,一般情况下,并不建议使用双重加锁机制,酌情使用才是正理!
public class Singleton {
private static volatile Singleton singleton = null;
private Singleton(){}
public static Singleton getSingleton(){
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
5)静态内部类法
public class Singleton {
private static class Holder {
private static Singleton singleton = new Singleton();
}
private Singleton(){}
public static Singleton getSingleton(){
return Holder.singleton;
}
}
把Singleton实例放到一个静态内部类中,这样就避免了静态实例在Singleton类加载的时候就创建对象,并且由于静态内部类只会被加载一次,所以这种写法也是线程安全的