定义:单例模式就是一个类只能有一个实例。
实现单例模式的必要条件:
1、类构造器必须是私有的
2、一个静态的该类对象
3、一个获取实例对象的静态方法
单例模式的使用场景:在一个系统中要求一个类有且仅有一个对象,如果出现多个对象就会出现“不良反应”,可以采用单例模式。
单例模式优缺点:
优点:内存开支少;减少了系统的性能开销;避免对资源的多重占用;可以在系统设置全局的访问点,避免对同一个资源文件进行写操作。
缺点:单例模式一般没有接口,所以扩展困难。
饿汉式
这种方式是,不管你会不会用到该类的实例,系统都会帮你创建好。
优点:线程安全,不用加锁,提高效率
缺点:类加载时就进行初始化,浪费内存
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getSingleton() {
return singleton;
}
}
懒汉式
这个方式会把实例对象的创建延迟到真正使用的时候。
线程不安全的方式
public class Singleton02 {
private static Singleton02 singleton;
private Singleton02() {
}
public static Singleton02 getSingleton() {
if (singleton == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton=new Singleton02();
}
return singleton;
}
}
通过以下代码可以验证线程不安全 :
@Test
public void dmeo1() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Callable<Singleton02> callable=new Callable<Singleton02>() {
@Override
public Singleton02 call() throws Exception {
return Singleton02.getSingleton();
}
};
Future<Singleton02> s1 = executorService.submit(callable);
Future<Singleton02> s2 = executorService.submit(callable);
System.out.println(s1.get()==s2.get());
}
线程安全的方式
双重校验锁
public class Singleton03 {
private static Singleton03 singleton;
private Singleton03() {
}
public static Singleton03 getSingleton() {
if (singleton == null) {
synchronized (Singleton03.class) {
if (singleton == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton=new Singleton03();
}
}
}
return singleton;
}
}
上面的代码使用双重检查机制,可以优化执行效率。使得之后调用该方法时,不需要在singleton不为null时,还去获取锁,之后再去同步代码块中检查,singleton是否为null。
内部类
public class Singleton04 {
private Singleton04() {
}
public static Singleton04 getSingleton() {
return InnerClass.singleton;
}
private static class InnerClass{
private static Singleton04 singleton=new Singleton04();
}
}
使用内部类实现单例模式就写起来很简单,效率也高。
内部类的加载时期是它第一次被使用的时候,而且类只能被加载一次。