JAVA单例模式
设计模式大题分为创建性模式,结构型模式,行为性模式
这节主要讲解单列模式,单列模式有以下八种创建方法:
1.饿汉式(静态常量)
2.饿汉式(静态代码块)
3.懒汉式(线程不安全)
4.懒汉式(线程安全,同步方法)
5.懒汉式(线程安全,同步代码块)
6.双重检查
7.静态内部类
8.枚举
1.饿汉式(静态常量)
优点就是写法比较简单,类装载的时候就完成了实例化,解决了类的同步问题
缺点:类装载就完成了类的初始化,可能这个类暂时不需要使用,这样就造成了内存浪费
public class SingletonTest01 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1==singleton2);
}
}
class Singleton{
private final static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
2.饿汉式(静态代码块)
优点和缺点和静态常量一样
public class SingletonTest02 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1==singleton2);
}
}
class Singleton{
private final static Singleton instance;
static {
instance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
3.懒汉式(线程不安全)
优点:线程使用的时候才进行加载,节约内存
缺点:线程不安全,在多线程的情况下有可能第一个线程进入if语句,但是还没有创建好,这个时候又进来第二个线程进行创建,那么他就创建了多个实例,破坏了单列模式的规则,
public class SingletonTest03 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1==singleton2);
}
}
class Singleton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
4.懒汉式(线程安全,同步方法)
优点:解决了线程安全问题
缺点:方法同步效率太低,不推荐使用
public class SingletonTest04 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1==singleton2);
}
}
class Singleton{
private static Singleton instance;
private Singleton(){}
public synchronized static Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
5.懒汉式(线程不安全,同步代码块)
多线程情况下可能会生成多个实例
public class SingletonTest05 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1==singleton2);
}
}
class Singleton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
instance = new Singleton();
}
}
return instance;
}
}
6.双重检查
首先来说,对于第一个if,只有在instance没有创建的情况下才能进去,也就是性能相对不错,即使第一个if有很多线程进来,但是对于synchronized只有一个线程可以进去,也就是对于进入第一个if的其他线程只有等待第一个线程执行完同步代码块,而且对于第一个线程执行完之后,其他线程在进去也不会创建实例了,所以他是延时加载,线程安全的
public class SingletonTest06 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1==singleton2);
}
}
class Singleton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
7.静态内部类
类装载是线程安全的,静态内部类只会在第一次使用的时候才会进行加载
public class SingletonTest07 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1==singleton2);
}
}
class Singleton{
private Singleton(){}
private static class InnerSingleton{
public static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return InnerSingleton.INSTANCE;
}
}
8.枚举
枚举本身就是单列的,所以不用考虑多线程安全这些安全问题(针对与单列模式)
public class SingletonTest08 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.INSTANCE;
Singleton singleton2 = Singleton.INSTANCE;
System.out.println(singleton1==singleton2);
singleton1.sayOK();
}
}
enum Singleton{
INSTANCE;
public void sayOK(){
System.out.println("ok~");
}
}
什么时候会使用单列模式:需要频繁创建和销毁的对象,创建对象耗时过多或耗费资源过多(重量级对象),但又经常用到的对象,工具类对象,频繁访问数据库或文件的对象(比如数据源,session等)
在JDK中的Runtime采用了单列模式,使用了饿汉式静态常量来进行创建
image.png
可以用到单例模式的地方线程池,数据库连接池,Spring里的IOC容器默认就是单例模式采用了ConcurrentHashMap存储bean