单例模式,最常见的就是饥饿模式,和懒汉模式,一个直接实例化对象,一个在调用方法时进行实例化对象。
饥饿模式:很饿,立即实例化对象
懒汉模式:很懒,用的时候才实例化对象
考虑到性能和线程安全问题,我们一般选择下面两种比较经典的单例模式,在性能提高的同时,又保证了线程安全
dubble check instance
static inner class
双重检查:
package com.bjsxt.base.conn011;
public class DubbleSingleton {
private DubbleSingleton(){}
private static DubbleSingleton ds;
public static DubbleSingleton getDs(){
//第一重检查:在多线程环境,相当没用,线程一下全进来了,没等得及第一个线程执行后面的代码
if(ds == null){
try {
//这里夸张一点:假设后面的实例化有3s时间,
//模拟初始化对象的准备时间...
//那么所有线程都等在这里了
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//加类锁,只有一个线程进来,其他都在这外面排队
synchronized (DubbleSingleton.class) {
//第二重检查:一定要加,因为很多线程还在外面排队,他们都已经过了第一重检查
//但第一个线程实例化完成,释放锁,就会有第二个线程进来,这时第一个线程已经实例化
//所以还要加一个判断
if(ds == null){
ds = new DubbleSingleton();
}
}
}
return ds;
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
//聪明的方法,用hashcode来判断对象是否是同一个,我没想到
System.out.println(DubbleSingleton.getDs().hashCode());
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(DubbleSingleton.getDs().hashCode());
}
},"t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(DubbleSingleton.getDs().hashCode());
}
},"t3");
t1.start();
t2.start();
t3.start();
}
}
静态类部类:
package com.bjsxt.base.conn011;
public class InnerSingleton {
private InnerSingleton(){}
private static class Singletion {
private static Singletion single = new Singletion();
}
public static Singletion getInstance(){
return Singletion.single;
}
}