前言
- 什么是单例
– 在应用的整个生命周期周期中只有一个实例对象。 - 应用场景及好处
– 对于一些频繁创建的对象、或者创建对象耗时过多,对象占用内存大,而且频繁使用的对象、工具类等,我们可以使用单例模式,这样能够节省资源以及提升系统性能。
单例之恶汉模式
- 静态常量方式
package com.dason.singleton.evilman.typeone;
public class EvilManOne {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2);
}
}
class Singleton{
// 私有构造
private void Singleton(){}
// 私有静态常量
private final static Singleton instance = new Singleton();
// 公共静态获取方法
public static Singleton getInstance(){
return instance;
}
}
- 静态代码块方式
package com.dason.singleton.evilman.typetwo;
public class EvilManwo {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2);
}
}
class Singleton{
// 私有构造
private void Singleton(){}
// 私有静态常量
private static Singleton instance = null;
// 静态代码块
static {
instance = new Singleton();
}
// 公共静态获取方法
public static Singleton getInstance(){
return instance;
}
}
恶汉模式优缺点
优点:利用类加载线程安全机制来进行实例化,从而达到线程安全。
缺点:没有进行懒加载,当程序不曾使用时,其实是一种资源的浪费。
单例之懒汉模式
- 线程不安全懒汉模式
package com.dason.singleton.lazyman.typeone;
public class LazyManOne {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2);
}
}
class Singleton{
// 私有构造
private void Singleton(){}
// 私有静态常量
private static Singleton instance = null;
// 公共静态获取方法
// 懒加载,当第一次是使用的时候才创建,从而也带来新问题:线程不安全
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
- 线程安全懒加载模式-同步方法
package com.dason.singleton.lazyman.typetwo;
public class LazyManTwo {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2);
}
}
class Singleton{
// 私有构造
private void Singleton(){}
// 私有静态常量; volatile 来声明使其线程可见性
private static volatile Singleton instance = null;
// 公共静态同步获取方法
// 懒加载,同步解决线程不安全问题
//同步带来新的问题:并发时带来效率低
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
- 同步代码块懒加载-双检查方式
package com.dason.singleton.lazyman.typethree;
public class LazyManThree {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2);
}
}
class Singleton{
// 私有构造
private void Singleton(){}
// 私有静态常量; volatile 来声明线程的可见性
private static volatile Singleton instance = null;
// 公共静态同步代码块获取方法
// 懒加载,同步代码块解决线程不安全问题
public static Singleton getInstance(){
if(instance == null){//没有该行判断也行,增加该判断大大提升并发时性能
synchronized (Singleton.class){
if(instance == null){//该行判断必须有,不然无法达到单例
instance = new Singleton();
}
}
}
return instance;
}
}
- 线程安全懒加载-静态内部类
package com.dason.singleton.lazyman.typefour;
public class LazyManFour {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2);
}
}
class Singleton{
// 私有构造
private void Singleton(){}
//私有的静态内部类:在类加载时,是不会加载类的静态内部类,从而达到懒加载
// 线程安全:当第一次使用时,JVM加载静态内部类,JVM类加载是线程安全的,从而实例化对象
private static class SingletonInstance {
private static Singleton INSTANCE = new Singleton();
}
// 公共静态获取方法
public static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
- 线程安全-枚举
package com.dason.singleton.lazyman.typefive;
/**
* @Author Dason
* @CLASS LazyManFive
* @DESC
* @CreateTime 2019-08-23
*/
public class LazyManFive {
public static void main(String[] args) {
Singleton instance = Singleton.INSTANCE;
}
}
// 使用枚举不仅能够解决线程安全问题,同时能够防止反序列化创建对象
enum Singleton{
INSTANCE;
}
推荐使用:枚举方式、静态内部类、同步代码块(双重检查)
扩展:JVM类装载机制?enum是怎么防止反序列化创建对象?volatile是怎么保证线程内存可见性?