设计模式之单例模式

来源:互联网 发布:淘宝颜色选择图片尺寸 编辑:程序博客网 时间:2024/04/29 20:13

转载请注明出处:http://blog.csdn.net/droyon/article/details/8715154

单例模式:确保一个类只有一个实例,并提供一个全局的访问点。

在单例模式下,当需要返回单个实例时,通过单件类获取是唯一的途径。

案例代码下载

情景:小明家只有一辆车,车在某一个时刻,只有一个状态,要么前进,要么后退,也就是倒车。

案例代码:

在正规的单例模式中,单例类需要提供私有的构造方法,通过共有的全局访问点。在本测试代码中为了比较差异,对单例模式稍作改动。

单例模式只允许创建一个对象,为了检查是不是一个对象,我把对象的hashCode打印出来了。

1、Test.java

public class Test {public static void main(String args[]){//小明家只有一辆汽车//一个类可以有好多个实例Car c_a = new Car();Car c_b = new Car();c_a.driverBackward();c_b.driverForward();System.out.println(c_a);//Car [mName=小明家的汽车, mState=倒车]hashCode373882728System.out.println(c_b);//Car [mName=小明家的汽车, mState=向前开]hashCode309858374System.out.println("------------------------------");//使用单例模式管理汽车//虽然创建了两个对象,但同时只有一个引用。Car c_1 = SingletonNumOne.getInstance();Car c_2 = SingletonNumOne.getInstance();c_1.driverBackward();System.out.println(c_1);//Car [mName=小明家的汽车, mState=倒车]hashCode241990244System.out.println(c_2);//Car [mName=小明家的汽车, mState=倒车]hashCode241990244System.out.println("-----------------------");c_2.driverForward();System.out.println(c_1);//Car [mName=小明家的汽车, mState=向前开]hashCode241990244System.out.println(c_2);//Car [mName=小明家的汽车, mState=向前开]hashCode241990244}}
2、SingletonNumOne.java//单例对象获取类

public class SingletonNumOne {private static Car car = null;public static Car getInstance(){if(car == null){try {Thread.sleep(2000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}car = new Car();}return car;}}
测试结果,我附在打印函数的后面了。

这种方式成为”延迟实例化“的方式,创建单例对象,这个模式在大部分状态下都工作正常,但如果在多线程系统中,就不那么正常了。

3、TestMultiThread.java

public class TestMultiThread {public static void main(String args[]){NewThread n1 = new NewThread();NewThread n2 = new NewThread();new Thread(n1).start();new Thread(n2).start();try {Thread.sleep(3000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}Car c1 = n1.getCar();Car c2 = n2.getCar();System.out.println(c1);//Car [mName=小明家的汽车, mState=null]hashCode1667685889System.out.println(c2);//Car [mName=小明家的汽车, mState=null]hashCode1987659426}}class NewThread implements Runnable{Car car_1;@Overridepublic void run() {car_1 = SingletonNumOne.getInstance();}public Car getCar(){return car_1;}}
输出结果,我附在打印函数后面了,通过hashcode,我们可以看到,这是两个不同的对象。不是说通过单例模式得到的都是同一个对象吗?这是怎么回事?

其实这是java同步并发引起的,试想多个线程,同时运行SingletonNumOne.getInstance()方法,每个线程在任何时刻都可能获取时间片并执行此函数,这就会产生,一个线程正在实例化new Car,另一个线程也运行到这里。

针对这个问题,有三种解决方案。

第一种解决方案:加同步锁

4、SingletonNumOne.java

public class SingletonNumOne {private static Car car = null;public static synchronized Car getInstance(){if(car == null){try {Thread.sleep(2000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}car = new Car();}return car;}}
第二种方式:使用”急切创建模式“得到单例对象

5、SingletonNumTwo.java

public class SingletonNumTwo {private static Car car = new Car();public static synchronized Car getInstance(){return car;}}

第三种:使用双重检查加锁的方式

6、SingletonNumThree.java

public class SingletonNumThree {private volatile static Car mCar;public static Car getInstance(){if(mCar == null){synchronized(Car.class){if(mCar == null){mCar = new Car();}}}return mCar;}}

Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
测试类:

7、Test2.java

public class Test2 {public static void main(String args[]){//第一种方式 SingletonNumOne在getInstance方法上加同步锁Car car1 = SingletonNumOne.getInstance();Car car2 = SingletonNumOne.getInstance();System.out.println(car1);//Car [mName=小明家的汽车, mState=null]hashCode613481760System.out.println(car2);//Car [mName=小明家的汽车, mState=null]hashCode613481760System.out.println("---------------------");//第二种方法 单例模式”急切“创建法Car car3 = SingletonNumTwo.getInstance();Car car4 = SingletonNumTwo.getInstance();System.out.println(car3);//Car [mName=小明家的汽车, mState=null]hashCode1987659426System.out.println(car4);//Car [mName=小明家的汽车, mState=null]hashCode1987659426System.out.println("---------------------");//第三种方法://双重检查加锁Car car5 = SingletonNumThree.getInstance();Car car6 = SingletonNumThree.getInstance();System.out.println(car5);//Car [mName=小明家的汽车, mState=null]hashCode2048243029System.out.println(car6);//Car [mName=小明家的汽车, mState=null]hashCode2048243029}}
测试结果我附在打印函数后面了。

总结:单件模式确保程序中一个类仅有一个实例对象,单件模式提供访问这个单件对象的全局访问点。在java中实现单件模式需要似有构造器和一个静态方法和一个静态变量。在使用单件模式的第一种方案时要处理好多线程引发的问题。