单例模式属于java设计模式的一种,最常见实现方式有以下几种 懒汉、饿汉、双重检查单例、静态内部类单例。
单例模式的特点:
1:单例类只能有一个实例
2:单例类的唯一实例化必须由自己完成
3:单例类给其他对象提供唯一实例
如何保证第一个和第三个特点呢->2个实例化的对象相等说明是同一实例化对象
1 public class SingletonTest {
2
3 public static void main(String[] args) {
4 Singleton singleton1=Singleton.getInstance();
5 Singleton singleton2=Singleton.getInstance();
6 /*
7 * 利用Set的特性检验2个对象是同一个实例
8 * 输出1代表这两个变量代表的同一个实例对象
9 *
10 */
11 Set set=new HashSet();
12 set.add(singleton1);
13 set.add(singleton2);
14 System.out.println("set长度"+set.size());
15 //set长度1
16 }
17 }
如何理解第二个特点:单例类是的实例化必须由自己完成->私有化构造器
private Singleton() {
}
1 package com.innerclass;
2
3 public class SingletonTest {
4
5 public static void main(String[] args) {
6 //我们在同包中创建一个其他类 并尝试创建Singleton实例 得的一个错误
7 //The constructor Singleton() is not visible
8 //构造方法Singleton() 是不可见的 也就是说我们无法创建Singleton的实例对象
9 Singleton singleton=new Singleton();
10
11 }
12 }
优点:始终只有一个singleton实例对象 所以线程安全
在类加载的同时已经创建好一个静态对象,调用时反应速度快
缺点:jvm加载类的时候一定会实例化,如果一直没调用getInstance()方法,会造成资源的浪费。
1 public class Singleton {
2 private Singleton() {
3 }
4 private static Singleton singleton=new Singleton();
5 public static Singleton getInstance() {
6 return singleton;
7 }
8 }
优点:按需加载,不会造成资源的浪费
缺点:无synchronized关键字的单例类会造成线程的不同步
1 private Singleton() {
2
3 }
4 public static Singleton singleton=null;
5 public synchronized Singleton getInstance(){
6 if(singleton==null) {
7 return new Singleton();
8 }
9 return singleton;
10 }
此处说一下为什么要给getInstance()方法加锁(实际意义上是给Singleton.class类类型加锁,有兴趣可以去了解一下)
假设上面的代码中没有 synchronized 关键字
public class Singleton {
private Singleton() {
}
private static Singleton singleton=null;
public static Singleton getInstance(){
if(singleton==null) {
try {
//假设线程阻塞情况
Thread.sleep(100);
return new Singleton();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return singleton;
}
public static void main(String[] args) {
Set singletons=
new HashSet();
for (int i = 0; i < 10; i++) {
singletons.add(Singleton.getInstance());
}
System.out.println(singletons.size());
//10
//说明多线程下懒汉式可能会创建多个实例对象
}
}
这种情况下,线程安全可以保证,但是效率问题受到人的诟病了。因为线程第一次实例化类之后,往后每次获取实例化对象仍然需要去获取单例类的锁和释放锁。增加了性能的损耗。于是有了以下2中进阶方式的单例模式
1 public class Singleton {
2 /**
3 * 双重检查单例
4 */
5 private Singleton() {
6
7 }
8 private static volatile Singleton singleton;
9 public static Singleton getInstance() {
10 if(singleton!=null) {
11 synchronized (Singleton.class) {
12 if(singleton!=null) {
13 singleton=new Singleton();
14 }
15 }
16 }
17 return singleton;
18 }
19 }
volatile关键字 在这里不做叙述,有兴趣的可以直接去百度它的作用
public class Singleton {
private Singleton() {
}
private static class SingletonHandler{
private static Singleton singleton=new Singleton();
}
public Singleton getInstance() {
return SingletonHandler.singleton;
}
}
推荐大家在多线程开发中使用双重检查单例和静态内部类单例,集成了懒汉和饿汉的优点。
如何只是单线程没有线程同步情况的话按照情况选择懒汉和饿汉式。
学习过程中,如有不对,请指出。