Java设计模式——单例模式

单例模式是一种大家比较熟悉的模式,就不多做介绍了。下面说一下多线程中遇到的问题吧
比如有一个计数器,每次输出都会自增1,常规单例模式的写法如下:

public class Counter {
    private static int sum;
    private static Counter counter;

    private Counter() {
        sum = 0;
    }

    public static Counter getInstance() {
        if (counter == null) {
            counter = new Counter();
        }
        System.out.println("sum:"+sum++);
        return counter;
    }
}

创建100个线程输出sum,测试代码如下:

public class Test {
public static void main(String[] args){
      for (int i = 0; i < 100; i++) {  
          new Thread(new Runnable() {  
              @Override  
              public void run() {  
                  Counter.getInstance();
              }  
          }).start();  
      }  
}
}

我们来看一下单例模式的定义:确保一个类只有一个实例,并提供一个全局访问点。
根据这个定义来看,输出的结果应该为按顺序为0到99,但是实际输出如下:
sum:0
sum:2
sum:1
sum:0
sum:3
sum:4
sum:5
sum:8
sum:7
sum:6
sum:10
sum:9
………………
后面就省略了,每次运行的结果应该也不一样。
后来思考了一下原因,可能是每个线程得到的对象不是同一个对象,于是在getInstance方法加入线程锁。

public static synchronized Counter getInstance() {
        if (counter == null) {
            counter = new Counter();
        }
        System.out.println("sum:"+sum++);
        return counter;
    }

这样看来问题似乎解决了,但是似乎这样的做法会性能很不好
如果应用程序总是创建并且使用单件实例,或者在创建和运行时不是很大的运算,可以这样修改代码:

public class Counter {
    private static int sum;
    private static Counter counter=new Counter();

    private Counter() {
        sum = 0;
    }

    public static synchronized Counter getInstance() {
        System.out.println("sum:"+sum++);
        return counter;
    }
}

这样的写法,JVM虚拟机在加载这个类时会马上创建这个类唯一的实例,保证任何线程访问前都会先创建这个实例,使用的也都是这个实例。
还有一种写法就是利用双重检查枷锁,在getInstance中减少使用同步。会先检查实例是否已经被创建,如果没有被创建才会进行同步。写法如下:

public class Counter {
    private static int sum;
    private volatile static Counter counter;
    private Counter() {
        sum = 0;
    }
    public static synchronized Counter getInstance() {//检查实例,如果不存在,就进入同步区域
        if (counter == null) {//只有第一次才彻底执行这些所有代码
            synchronized (Counter.class) {
                counter = new Counter();
            }
        }
        System.out.println("sum:" + sum++);
        return counter;
    }
}

测试结果也是正常的0到99
volatile不适用于jdk1.4以前。
总结:
不可以把单例类当成父类设计出子类。因为继承单例类的构造器是私有的,不能用似有构造器来扩展类,所以必须把构造器变成共有的,但是变成共有的之后,就不能保证只有一个实例了。还有一个问题就是,单例的实现是利用静态变量,直接结成会导致所有派生类共同使用一个实例变量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值