单例模式
定义
保证一个类仅有一个实例,并且提供一个访问它的全局访问点。
UML
对象说明
Singleton
:定义一个getInstance()
操作,允许客户访问它的唯一实例。getInstance()
是一个静态方法,主要负责创建自己的唯一实例。
Demo
public class SingletonTest { public static void main(String[] args) { //超人对象应该是唯一的。保证superMan的唯一性。可使用单例模式解决。 Superman superman=Superman.getInstance(); superman.func(); superman.setName("iron man"); superman.func(); } } class Superman{ /** * 确定英雄的名字 * */ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 自己创建一个实例,供外界调用,保证对象的唯一性 * */ private static Superman superman=new Superman("Hero"); /** * 私有化构造函数 * @param name :超人的姓名 * */ private Superman(String name) { this.name = name; } /** * 返回用户所希望调用的实例 * */ public static Superman getInstance(){ return superman; } /** * 确定蜘蛛侠的能够实现的方法 * */ public void func(){ System.out.println(this.name+" fly..."); } }
单例模式实现步骤分析
1,一个类只要提供了构造函数,就可以产生多个对象。完全无法保证唯一。那么也就无法保证对象的唯一性。既然对象数量不可控,干脆,不让其他程序建立对象。
2,不让其他程序创建,对象何在? 自己在本类中创建一个对象,这样好处是什么,可控。
3,创建完成后,是要给其他程序提供访问的方式。就是访问这个对象的方式。
单例模式实现的两种形式
//饿汉式 class Single { //2,创建一个本类对象。 private static /*final*/ Single s = new Single(); //1,私有化构造函数。 private Single(){} //3,定义一个方法返回这个对象。 public static Single getInstance() { return s; } } //懒汉式 //单例的延迟加载方式。面试最多的是懒汉式。 class Single2 { private static Single2 s2 = null; private Single2(){} public static Single2 getInstance() { if(s2==null) s2 = new Single2(); return s2; } }
单例模式在多线程里面的操作
在多线程的程序中,多个线程同时访问Singleton
,调用getInstance()
方法的时候,会有可能创建多个实例。那么在实际的操作过程中,我们可以给进程来加一把锁lock
来确定。lock
是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果有其他的线程试图进入锁定的代码,则它将一直等待,直到该对象被释放。
基于synchronize的实现
public class Singleton1 { private static Singleton1 instance=null; private Singleton1() { } public static Singleton1 getInstance(){ if (instance==null){ //采用synchronized关键字来保证同步 synchronized (Singleton1.class){ if (instance==null){ instance=new Singleton1(); } } } return instance; } }
采用双重锁定的原因:当instance
存在时,直接返回,不创建实例,这没有问题,当instance
为null
的时候,并且存在两个线程同时调用getInstance()
方法的时候,它们都可以通过第一层的判断,然后由于锁机制,只有一个线程可以进入创建实例,创建完成后释放锁,但是第二个线程仍然可以去创建实例,这就没有达到我们单例模式的要求,所以需要双重锁定。
学习Lock
class BoundedBuffer { final Lock lock = new ReentrantLock();//锁 final Condition notFull = lock.newCondition(); //生产 final Condition notEmpty = lock.newCondition(); //消费 final Object[] items = new Object[100];//存储商品的容器。 int putptr/*生产者使用的角标*/, takeptr/*消费者使用的角标*/, count/*计数器*/; /*生产者使用的方法,往数组中存储商品*/ public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length) //判断计数器是否已到数组长度。满了。 notFull.await();//生产就等待。 items[putptr] = x; //按照角标将商品存储到数组中 if (++putptr == items.length) //如果存储的角标到了数组的长度,就将角标归零。 putptr = 0; ++count;//计数器自增。 notEmpty.signal();//唤醒一个消费者 } finally { lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) //如果计数器为0,说明没有商品,消费者等待。 notEmpty.await(); Object x = items[takeptr]; //从数组中通过消费者角标获取商品。 if (++takeptr == items.length) //如果消费的角标等于了数组的长度,将角标归零。 takeptr = 0; --count;//计数器自减。 notFull.signal();//唤醒生产者。 return x; } finally { lock.unlock(); } } }
小结
单例设计模式保证了一个类的对象的唯一性。比如多个程序都要使用一个配置文件中的数据,而且要实现数据共享和交换。必须要将多个数据封装到一个对象中。而且多个程序操作的是同一个对象。那也就是说必须保证这个配置文件对象的唯一性。