什么是单例模式
但是模式是在软件编程中常见的一种设计模式,并且是代码量很少的一种,这中设计模式并不是只限制于一种语言,这是在无数前辈哪里总结出来的一种软件设计思想,顾名思义单例模式在系统中只有一个实例化的对象,正如他的名字一样只有一个,单例模式最初出现在《设计模式》:保证一个类只有一个实例,并且提供一个访问他的全局访问点。
单例模式分为哪几种
1.饿汉式
2.懒汉式
3.最后我还要说下在多线程的模式下如何保证只能实例一个对象
饿汉式写法
饿汉式写法我想应该理解成像一个饥饿的汉子一样,不管在实际的情况需不需要创建这个类的对象,总之在第一次使用该类的时候就创建一个对象引用
如何写?我这里总结的3个步骤:
- 私有化构造方法
- 提供一个公共的方法,这个方法可以返回该类的实例对象
- 创建一个私有化的静态的类的对象
问:为什么要设置私有化的静态类对象?
答:如果这里的对象时公有的那么我们可以通过在外界类名.对象名的修改这个对象,虽然说我们的构造方法是私有的我们不能new一个对象,但是我们可以将此设置为空,像下图似的如果是公有的就可以在外界修改了,这样很危险,所以这里要设置成私有的。
接下来我们看下代码如何编写和检测是否是整个程序只有一个对象
class A{ //创建一个私有静态的类对象 private static A a = new A(); //私有化构造 private A() { System.out.println("创建了一个A的实例对象"); } //提供一个共有的可以返回类对象的方法 public static A getSingleton() { return a; }}
在外界调用getSingleton方法来获取创建的对象
A.getSingleton();
接下来测试下是否只是创建了一个对象,我们这里通过For循环连续调用getSingleton方法,我在A类的构造方法里输出了一句话如果是创建了多个对象,肯定会多次输出这句话
public static void main(String[] args) { for(int i = 0 ;i <500;i++) { A.getSingleton(); }}
测试是否只有一个对象被实例化,结果输出如下,以此证明这样我们在整个程序中只是创建了一个对象
懒汉式写法
懒汉式写法可能是说这个程序比较懒吧,只有需要的时候他才实例化一个对象,这个部分代码是和饿汉式差不多的,只是在实例化对象的时候有所区别,懒汉式写法的步骤如下:
- 私有化构造方法
- 提供一个公共的方法,这个方法可以返回该类的实例对象
- 创建一个私有化的静态的类的对象 这里创建类的对象的时候不要直接new出来
- 在我们的返回对象的公共方法中写一个判空的处理
来看下代码如何实现的
class A{ //创建一个私有静态的类对象 private static A a = null; //私有化构造 private A() { System.out.println("创建了一个A的实例对象"); } //提供一个共有的可以返回类对象的方法 public static A getSingleton() { if(a == null) { a = new A(); } return a; }}
懒汉式的调用方法和饿汉式的一样
A.getSingleton();
这里我们也是通过for循环来测试下是否只有一个对象被实例化
public static void main(String[] args) { for(int i = 0 ;i <500;i++) { A.getSingleton(); }}
打印结果
在多线程中懒汉式写法出现的问题
public static void main(String[] args) { for(int i = 0 ;i <500;i++) { Thread thread = new Thread(()->{ A.getSingleton(); }); thread.start(); }}
这是我们通过多线程来获取到的A类的对象,来看下打印出的结果,这里并没有像上面的写法中显示的结果一样,而是创建了好多的实例对象
在学习了多线程的朋友应该了解到,其实所谓的并发,并不是真正的并发,而是通过时间片的轮转来使用cpu的资源,而这里的问题就出现在了判空处理的代码中
这里画个图来解释下:
解决这一问题我们需要用到了多线程中的synchronized关键字同步方法,这里要看清楚,因为这里的公共方法是static静态的方法,所以这里的同步监视器是指的A这个类,
这里使用同步方法可以看出是在操作系统中的临界资源,当一个数据正在被访问,会给这个数据加一把锁,有锁的数据只能同时被一个进行所访问,其他的线程只能等待,只有这个线程释放了这把锁其他的线程才可以访问,这样我下面这些代码在做判空处理的时候就不会出现错误。
class A{ //创建一个私有静态的类对象 private static A a = null; //私有化构造 private A() { System.out.println("创建了一个A的实例对象"); } //提供一个共有的可以返回类对象的方法 public synchronized static A getSingleton() { if(a == null) { a = new A(); } return a; }}
同样这里使用for循环在多线程下来检测下是否可以
public static void main(String[] args) { for(int i = 0 ;i <500;i++) { Thread thread = new Thread(()->{ A.getSingleton(); }); thread.start(); }}
看下结果,这中情况只会出现在懒汉式的写法中,饿汉式是不会出现的。只要加个同步方法就可以了(这里的同步静态方法监视器是类)