一、什么是单例
单例模式顾名思义只能获取一个实例,那么想要获取实例就需要使用到构造方法,那么我们如何达成只有一个实例呢?
就是不让其他的类来使用我们的构造函数,我们可以将构造函数设置为private私有的,还要在单例类内预先放置一个我们
想对外提供的单一实例。并且向外提供一个访问到该单例类的方法或者将预留的单例设置为类的成员变量。
二、常用的几种单例实现
1、饿汉式
所谓的饿汉式可以这样理解,饿了就需要吃那么东西就必须有,所以就是立即加载也就是必须先new一个实例放在那里。
采取的是空间换时间的方式,用不用都会创建,如果这样的类多了占用内存空间。
具体实现代码如下并且附有测试代码:
package com.yang.review; /** * * @author 阳 * */ public class MySingleton{ //饿汉式也就是立即加载 private static MySingleton mySingleton = new MySingleton(); //不存在线程安全问题。 public static MySingleton getInstance(){ return mySingleton; } public static void main(String[] args) { Thread thread1 = new Thread(){ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"__"+MySingleton.getInstance().hashCode()); } }; Thread thread2 = new Thread(){ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"__"+MySingleton.getInstance().hashCode()); } }; Thread thread3 = new Thread(){ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"__"+MySingleton.getInstance().hashCode()); } }; thread1.start(); thread2.start(); thread3.start(); } }
2、懒汉式
所谓的懒汉式顾名思义就是懒。也就是说什么时候用什么时候再去new要不想不到要去new,它采用的是时间换空间的
方式。由于这种方式的懒我们也就它为延迟加载。
具体的实现代码并且附有测试代码:
package com.yang.review; /** * * @author 阳 * */ public class MySingleton{ private static MySingleton mySingleton = null; public MySingleton() { super(); // TODO Auto-generated constructor stub } public static MySingleton getInstance(){ //这种方式存在线程安全问题如果多个线程执行到这个地方 //判断mySingleton == null 都去执行 new MySingleton(); //这时就会出现多个MySingleton的实例。 if(mySingleton != null){ }else{ mySingleton = new MySingleton(); } return mySingleton; } public static void main(String[] args) { Thread thread1 = new Thread(){ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"__"+MySingleton.getInstance().hashCode()); } }; Thread thread2 = new Thread(){ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"__"+MySingleton.getInstance().hashCode()); } }; Thread thread3 = new Thread(){ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"__"+MySingleton.getInstance().hashCode()); } }; thread1.start(); thread2.start(); thread3.start(); } }
这种方式存在线程安全问题结果如下:
在上图我们发现运行出的三个实例的hashcode值不完全一样那么这时就出现了线程安全问题。
解决方法一:使用synchronized关键字对于getInstance()方法进行同步。修正后的代码如下:
package com.yang.review; /** * * @author 阳 * */ public class MySingleton{ private static MySingleton mySingleton = null; public MySingleton() { super(); // TODO Auto-generated constructor stub } /* * 使用synchronized关键字进行同步 */ public static synchronized MySingleton getInstance(){ //这种方式存在线程安全问题如果多个线程执行到这个地方 //判断mySingleton == null 都去执行 new MySingleton(); //这时就会出现多个MySingleton的实例。 try { if(mySingleton != null){ }else{ //模拟较长时间的操作 Thread.sleep(2500); mySingleton = new MySingleton(); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return mySingleton; } }
解决方法二:使用同步代码块的方式,修正代码如下:package com.yang.review; /** * * @author 阳 * */ public class MySingleton{ private static MySingleton mySingleton = null; public MySingleton() { super(); // TODO Auto-generated constructor stub } /* * 使用synchronized关键字进行同步 */ public static synchronized MySingleton getInstance(){ synchronized (MySingleton.class) { if(mySingleton != null){ }else{ mySingleton = new MySingleton(); } } return mySingleton; } }
这种方式和上一种方式是一样的,效率都是很低的。
解决方法三:此方法和第二种差不多但是,同步的区域减小了,只对于new MySingleton(); 进行同步。
解决方法四:使用DCL(Double Check Lock)双检查锁进行线程安全。 代码如下:
package com.yang.review; /** * * @author 阳 * */ public class MySingleton{ /* * 使用volatile关键字保证了该引用的可见性 */ private volatile static MySingleton mySingleton = null; public MySingleton() { super(); // TODO Auto-generated constructor stub } /* * 使用synchronized关键字进行同步 */ public static synchronized MySingleton getInstance(){ synchronized (MySingleton.class) { try { if(mySingleton != null){ }else{ Thread.sleep(2500); /* * 只对于需要同步的部分进行同步 */ synchronized (MySingleton.class) { if(mySingleton == null) mySingleton = new MySingleton(); } } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return mySingleton; } }
3、使用静态内部类来实现单例模式
这种方式的具体实现代码如下:
package com.yang.review; /** * * @author 阳 * */ public class MySingleton{ //使用静态内部类方式 private static class MyInnerSinglteon{ private static MySingleton mySingleton = new MySingleton(); } public MySingleton() { super(); // TODO Auto-generated constructor stub } public static MySingleton getInstance(){ return MyInnerSinglteon.mySingleton; } public static void main(String[] args) { Thread thread1 = new Thread(){ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"__"+MySingleton.getInstance().hashCode()); } }; Thread thread2 = new Thread(){ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"__"+MySingleton.getInstance().hashCode()); } }; Thread thread3 = new Thread(){ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"__"+MySingleton.getInstance().hashCode()); } }; thread1.start(); thread2.start(); thread3.start(); } }
这种方式利用了classloader的类加载机制,而且对于内部类在没有引用到的时候是不会加载的,
这样就实现了懒加载的模式。