Java设计模式之单列模式

简言

单列模式:单一,也就是说一个类只能有一个对象。就类似于有些软件只能打开一次,当需要保证一个对象在内存中的唯一性时,就需要引入单列模式。

实现步骤

创建单列模式分三步操作:

1.将构造函数私有化

2.在类中创建一个本类对象

3.提供一个公有的接口来返回创建的类

相关说明

我们访问类里面的数据时,分两种情况:

1.通过实例化对象,然后通过对象的引用“.”出方法或属性。

2.通过类名“.”调用类里面静态的方法或属性。

当我们将类的构造方法私有化时,此时我们无法在类外实例化对象,所以只能通过第二种方法在内部创建一个static修饰的该类对象,然后定义一个static修饰的公有的方法将创建的类返回出去。static修饰的数据是跟随整个类的,在类加载的时候跟着类一起创建,由于构造函数私有化了,所以这个类也就创建了一次。

饿汉式

创建一个Singlehungry类

package com.single_mode;
/**
 * 单列模式:一个类只能被创建一次,将类的构造方法私有化,提供公有的返回该对象的方法
 *
 *      饿汉式:加载类的时候就创建了创建了一个Single实例
 */
public class Singlehungry {
    private String name;

    private int age;

    public  String getName() {   return name;  }

    public  int getAge() {  return age;  }

    public  void setName(String name) {  this.name = name; }

    public  void setAge(int age) {  this.age = age; }

    private Singlehungry(){ }//将构造函数私有化

    private static Singlehungry single=new Singlehungry();

    public static Singlehungry getInstance(){
        return single;
    }//提供公有访问的接口,将该对象返回出去
}

创建一个测试类Main类

package com.single_mode;

public class Main{
    public static void main(String[] args) {
        //饿汉式
        Singlehungry single1=Singlehungry.getInstance();
        single1.setName("筱静");
        single1.setAge(20);
        System.out.println("single1:"+single1.getAge()+"  "+single1.getName());
        Singlehungry single2=Singlehungry.getInstance();
        System.out.println("single2:"+single2.getAge()+"  "+single2.getName());
    }
}

运行结果:


single1和single2是指向的同一个对象

让我们画张图来了解一下:


当我们Singlehungry.加载类时,这个类的静态属性在方法区也就随着加载,首先在堆区new了一个Singlehungry的对象,假设它的地址为0x10,方法区的single保存了堆区Singlehungry对象的地址,即single=0x10。然后在栈区调用getInstance()方法,返回single,并用single1来接收,single1也就指向堆区的Singlehungry对象,然后set进行赋值。再次调用getInstance()方法时,调用之前加载好的Singlehungry对象,也就是single2=single=0x10。所以再次get时,它的值是相同的。

懒汉式

创建一个Singlelazy类

package com.single_mode;
/**
 * 懒汉式:
 */
public class Singlelazy {
    private String name;
    private int age;
    private static Singlelazy singlelazy=null;
    private Singlelazy(){ }
//    public static Singlelazy getInstence(){   //线程不安全
//        if(singlelazy==null)                
//            singlelazy=new Singlelazy();
//        return singlelazy;
//    }
    public static Singlelazy getInstence(){
        if(singlelazy==null) {
            synchronized(Singlelazy.class) {
                if (singlelazy == null)
                    singlelazy = new Singlelazy();
            }
        }
        return singlelazy;
    }
    public  String getName() {  return this.name; }
    public  int getAge() {  return age;  }
    public  void setName(String name) {   this.name = name; }
    public  void setAge(int age) {    this.age = age;  }
}

在我们用懒汉式发生了延时加载,首先方法区里面singlelazy赋值为null,在调用getInstence()方法时先判断一下,然后创建对象,而饿汉式在加载类的同时single就在堆区new了一个对象,这也是它们的区别所在。


由于方法区是线程共享的,懒汉式又有if判断然后创建对象。因此在多线程的情况下可能存在线程安全问题,需要线程同步用到synchonized关键字。如果将getInstence()整个函数个锁住,开销可能比较大,不太可取。可直接将判断那部分锁住即可。

或许你会问为什需要加上两个if判断?

假设有A,B,C三个线程,首先A线程抢到了CPU的执行权,执行 singlelazy==null   再执行下一个singlelazy==null 并且锁上,当A刚第二次判断时,CPU的执行权可能被C抢到了(CPU高速不断的切换线程,给我们的感觉每个进程异步执行),C线程进行第一层判断,由于第二层if上锁了,在A线程执行完毕之前 ,C线程无法继续执行的,只能等待了。C在等待的过程中CPU可能又切到了A线程,A线程接着执行,当A线程创建对象后,把锁打开 return singlelazy;。B线程得到了CPU的执行权,第一轮if判断结束false 就直接return singlelazy;最后C线程的到CPU执行权,由于A线程已把锁打开,C线程继续刚才的执行,进行第二次判断false,return singlelazy;最终得到的是A线程所创建的一个对象,解决了线程互斥问题。


每日鸡汤:耐得住寂寞、经得起诱惑。你会发现通往成功的道路并不拥挤,因为能坚持到最后的没有几个!


Over ! 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值