在学习单例模式前,不妨问自己几个问题:单例模式是怎么来的,单例模式怎么去用?
单例模式是怎么来的?
这就从设计模式起源开始,他是在实际实践中遇到类似情况可以通用经验所得到的总结,一般在其他模块或者方法多次调用类对象,也就是公共模块,用单例模式可以减少内存的消耗。
单例模式怎么去用?
那这个容易,然后很快不到一分钟写完了。
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
1 public class singleTonEx01 { 2 3 private static singleTonEx01 singleTon; 4 5 public static void main(String args[]) { 6 singleTonEx01 test1=singleTonEx01.getInstance(); 7 test1.CommonMethod(); 8 } 9 10 // 防止使用new构造函数实例化对象 11 private singleTonEx01() { 12 13 } 14 15 public static singleTonEx01 getInstance() { 16 if(null==singleTon) 17 singleTon=new singleTonEx01(); 18 return singleTon; 19 } 20 21 public void CommonMethod() { 22 System.out.println(getClass().getName()); 23 } 24 25 }
但这里会有问题,啥问题呢?单线程中是没问题,但是多线程会出现问题,假设两个多线程A与B,A与B同时判断singleTon不为空,那会创建两次,那对于这个问题需要使用同步锁去解决了,下面改造下代码。
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
public class singleTonEx01 { private static singleTonEx01 singleTon; public static void main(String args[]) { singleTonEx01 test1 = singleTonEx01.getInstance(); test1.CommonMethod(); } // 防止使用new构造函数实例化对象 private singleTonEx01() { } public static singleTonEx01 getInstance() { synchronized (singleTonEx01.class) { if (null == singleTon) singleTon = new singleTonEx01(); } return singleTon; } public void CommonMethod() { System.out.println(getClass().getName()); } }
这还不是最好的方法,如果是这样的话每个线程都会使用同步锁代码块,使用同步锁其实是消耗资源的,因此,可以再改进下,在外面锁再加个判断,这样一来提高了效率。(推荐这种写法1)
另外,补充一下,实例变量加上volatile的意义。
创建对象可以分解为如下的3行伪代码memory=allocate(); //1:分配对象的内存空间ctorInstance(memory); //2:初始化对象instance=memory; //3:设置instance指向刚分配的内存地址上面3行代码中的2和3之间,可能会被重排序导致先3后2;
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
public class singleTonEx01 { private volatile static singleTonEx01 singleTon; public static void main(String args[]) { singleTonEx01 test1 = singleTonEx01.getInstance(); test1.CommonMethod(); } // 防止使用new构造函数实例化对象 private singleTonEx01() { } public static singleTonEx01 getInstance() { if (singleTon == null) { synchronized (singleTonEx01.class) { if (null == singleTon) singleTon = new singleTonEx01(); } } return singleTon; } public void CommonMethod() { System.out.println(getClass().getName()); } }
其实还有另外两种方法,一种是不管有没有调用都实例化(俗称恶汉式),另一种是静态内部类的方法(推荐这种写法2),线程安全而且高效
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
public class singleTonEx02 { private static final singleTonEx02 singleTon=new singleTonEx02(); public static void main(String args[]) { singleTonEx02 test1 = singleTon; test1.CommonMethod(); } // 防止使用new构造函数实例化对象 private singleTonEx02() { } public static singleTonEx02 getIntance() { return singleTon; } public void CommonMethod() { System.out.println(getClass().getName()); } }
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
public class singleTonEx03 { private static class SingleNBClass { private final static singleTonEx03 singleTonEx03=new singleTonEx03(); } public static void main(String args[]) { singleTonEx03 test1 = singleTonEx03.getIntance(); test1.CommonMethod(); } // 防止使用new构造函数实例化对象 private singleTonEx03() { } public static final singleTonEx03 getIntance() { return SingleNBClass.singleTonEx03; } public void CommonMethod() { System.out.println(getClass().getName()); } }