目录
- 1 什么是单例模式?
- 2 我对饿汉式的思考
- 3 懒汉式
- 3.1 解决懒汉式的线程安全问题
- 3.1.1 加锁:synchronized(synchronized修饰静态方法)
- 3.1.2 对“3.1.1”性能的改进
1 什么是单例模式?
1.1 什么是饿汉式?
1.2 什么是懒汉式?
2 我对饿汉式的思考
public class LearnSingleton {private static LearnSingleton instance = new LearnSingleton();private LearnSingleton() {}public static LearnSingleton getInstance() {return instance;}}
- 当我们调用LearnSingleton.getInstance()时,会触发LearnSingleton的加载。在类加载的准备阶段,创建好了静态变量表(此时instance对应的slot槽还为null),等到了初始化阶段,开始执行clinit方法,会创建LearnSingleton的示例(执行了
new LearnSingleton()
语句)。类加载后,执行getInstance()方法,将单例返回给用户。- 这么一看,上述写法不也是懒汉式吗?在使用时才创建实例啊。(虽然,这个实例也是在类加载时就创建好的)
- 要想明白这到底是懒汉式,还是饿汉式,关键在于:
public class LearnSingleton {private static LearnSingleton instance = new LearnSingleton();private LearnSingleton() {}public static LearnSingleton getInstance() {return instance;}public static int add(int x, int y) {return x + y;}}
- 当用户调用LearnSingleton.add(1, 2)时,在类加载过程中,就已经创建好了单例,但并未使用。因此,这不符合在需要时创建单例的定义。从这个例子,就能想明白了,这种写法是饿汉式。
- 彻底的饿汉式:
public class LearnSingleton {private static final LearnSingleton instance = new LearnSingleton();private LearnSingleton() {}public static LearnSingleton getInstance() {return instance;}}
- 只要这个类加载了,由于instance是常量,因此在类加载的准备阶段就创建好了单例。这是彻底的饿汉式。可谓“饿疯了”。
- 饿汉式,是在类加载阶段完成实例的创建,由JVM保证了线程安全。
3 懒汉式
public class LearnLazySingleton {private static LearnLazySingleton instance;private LearnLazySingleton() {}public static LearnLazySingleton getInstance() {if (instance == null) {instance = new LearnLazySingleton();}return instance;}}
3.1 解决懒汉式的线程安全问题
3.1.1 加锁:synchronized(synchronized修饰静态方法)
public class LearnLazySingleton {private static LearnLazySingleton instance;private LearnLazySingleton() {}public synchronized static LearnLazySingleton getInstance() {if (instance == null) {instance = new LearnLazySingleton();}return instance;}public synchronized static int add(int x, int y) {return x + y;}public static int sub(int x, int y) {return x - y;}}
- 当线程A先调用getInstance()方法的同时, 另一个线程B尝试访问add()方法,线程B会因为没有LearnLazySingleton的class对象的锁而等待。如果类中的其他方法不是synchronized的,它们就不会被锁定,即线程C调用sub方法()就不会等待。
- 本质是因为,在调用同步方法前,只有获取锁,才能进入临界区。而如果不是临界区,那就不会受影响。
3.1.2 对“3.1.1”性能的改进
- 上面的写法,有个非常难受的地方,例如,线程A已经调用getInstance()方法,创建好了单例。但线程B为了获取单例,也不得不调用getInstance()方法(唯一获取实例的入口),这时候就可能和线程C撞车,因为只要线程C调用add方法(),就可能让线程B获取单例时发生阻塞。
- 改进:
public class LearnLazySingleton {private static LearnLazySingleton instance;private LearnLazySingleton() {}public static LearnLazySingleton getInstance() {if (instance == null) {synchronized (LearnLazySingleton.class) {if (instance == null) {instance = new LearnLazySingleton();}}}return instance;}public synchronized static int add(int x, int y) {return x + y;}}
- 在创建单例前,仍然存在线程A和线程B争抢着执行“instance = new LearnLazySingleton();”,也会影响线程C去调用add()方法。但一旦实例创建好后,instance不为null,线程们调用getInstance()方法就不会阻塞了。