java设计模式之单例模式详解
单例模式定义及应用场景
使用单例模式,规定全局只能有一个实例,并且提供一个全局访问站点。单例模式在java中的应用场景很多,比如说JAVAEE的servletContext,spring的ApplicationContext,数据库连接池。
饿汉式单例模式
饿汉式单例模式也是提供一个全局实例,只是全局实例在初始化时就加载了,不管有没有用到。
代码示例
public class HungryStaticSingleton {
//先静态后动态
//先上,后下
//先属性后方法
private static final HungryStaticSingleton hungrySingleton;
//装个B
static {
hungrySingleton = new HungryStaticSingleton();
}
private HungryStaticSingleton(){
}
public static HungryStaticSingleton getInstance(){
return hungrySingleton;
}
}
饿汉式单例模式定死了初始化时就做创建对象,适用于系统中单例对象较少的场景,由于定死了一个对象,所以在多线程的环境下是安全的。但是在单例对象较多的情况下会占用较大的内存空间。
懒汉式单例模式
因为懒汉式单例模式在类一创建时就会创建对象,当系统中的单例对象过多时,就会占用大量的内存空间。所以这边采用懒汉式单例模式来解决,懒汉式单例模式和饿汉式单例模式一样,构造方法私有化,在类中提供静态公有构造方法提供唯一实例。与饿汉式单例模式不同的是,懒汉式单例模式的对象要在使用的时候才创建。
代码示例
public class LazySimpleSingletion {
private static LazySimpleSingletion instance;
private LazySimpleSingletion(){
}
public static LazySimpleSingletion getInstance(){
if(instance == null){
instance = new LazySimpleSingletion();
}
return instance;
}
}
这种情况解决了系统运行就初始化对象的问题,但是存在线程安全问题。我们来开启两个线程来同时访问该实例。
public class LazySimpleSingletionExcutor implements Runnable{
@Override
public void run() {
LazySimpleSingletion instance = LazySimpleSingletion.getInstance();
System.out.println(Thread.currentThread().getName()+": "+instance);
}
}
public class TestLazySimpleSingletion {
public static void main(String[] args) {
Thread thread1 = new Thread(new LazySimpleSingletionExcutor());
Thread thread2 = new Thread(new LazySimpleSingletionExcutor());
thread1.start();
thread2.start();
System.out.println("end");
}
}
在线程跑的位置打上线程断点,然后以debug形式运行。
可以发现线程1和线程0同时运行,势必会出现多线程安全问题问题。
测试运行结果:
这里只有两个线程,在线程量过多的情况下,是有可能出现多个不同的实例的。为了解决这种问题,我们可以在方法上加上synchronized关键字。
public class LazySimpleSingletion {
private static LazySimpleSingletion instance;
private LazySimpleSingletion(){
}
public static synchronized LazySimpleSingletion getInstance(){
if(instance == null){
instance = new LazySimpleSingletion();
}
return instance;
}
}
再次运行代码,进入断点调试,可以发现一个进程进入getInstance方法时,另一个线程变成了monitor状态,发生了线程阻塞,这样就可以保证线程安全。可以发现线程安全问题得到解决。
但是当访问线程数量过多时,就会产生一个个的线程阻塞,导致我们程序的运行效率越来越慢,为了解决这个问题,我们采用双重锁机制。
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton instance;
private LazyDoubleCheckSingleton(){
}
public static LazyDoubleCheckSingleton getInstance(