单例的两种实现:
1、立即加载(饿汉模式)
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return singleton;
}
}
在方法调用之前,就已经创建好了实例,故不存在多线程安全的问题。
2、延迟加载(懒汉模式)
public class Singleton {
private static Singleton singleton ;
private Singleton(){
}
public static Singleton getInstance(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
延迟加载的缺点:在多线程的情况下,无法保证单例,会创建出“多例”。
测试:
public class Singleton {
private static Singleton singleton ;
private Singleton(){
}
public static Singleton getInstance(){
try {
if(singleton == null){
Thread.sleep(3000);
singleton = new Singleton();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return singleton;
}
}
public class MyThread extends Thread {
@Override
public void run() {
System.out.println(Singleton.getInstance().hashCode());
}
}
public class Run {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
MyThread myThread3 = new MyThread();
myThread1.start();
myThread2.start();
myThread3.start();
}
}
测试结果;
结果打印出了两种hashCode,说明创建了两个对象,并非单例。
解决方案:
1、采用同步方法
synchronized public static Singleton getInstance(){
try {
if(singleton == null){
Thread.sleep(3000);
singleton = new Singleton();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return singleton;
}
此方法是直接锁住了一个方法,粒度有点大,同步运行,效率非常低,当下一个线程想要获取对象,必须得等到上一个线程释放锁,才可以继续执行。
2、使用双检查锁
public static Singleton getInstance(){
try {
if(singleton == null){
Thread.sleep(3000);
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return singleton;
}
此方法只是锁住了部分代码,效率明显提高。