什么是单例模式?
The Singleton Pattern 确保一个类只能有一个实例,并且提供给全局去使用它。
- 我们创造一个类并且使它自己去管理自己的一个单例,我们也得阻止其它的类里面声称这个类的实例,如果获得一个实例,必须通过这个类本身去创建。
- 我们需要给全局提供一个入口去获得这个类的实例:当我们需要这个实例,只是查询该类,该类就会返回一个单例。
为什么要使用单例模式?
有许多的对象,往往都仅需要一个实例,比如线程池、缓存、对话框、处理参数和注册设置的对象、日志对象和一些扮演驱动设备去服务的对象,比如打印机和显卡等,像这些类型的对象,如果我们实例化多于一个这样的实例,我们会运行出这样一系列的错误,像错误的编码方式,内存溢出,前后不一致的结果等等。
下面看一下单例模式的实现:
public class Singleton {
private static Singleton uniqueInstanse;
private Singleton() {
}
public static Singleton getInstance() {
if (uniqueInstanse == null) {
uniqueInstanse = new Singleton();
}
return uniqueInstanse;
}
}
关于jvm
我们有两个线程,同时各自处理这段代码
ChololateBoiler boiler = ChocolateBoiler.getInstance();
fill();
boil();
drain();
程序执行getinstance()方法和给唯一实例的赋值的顺序是不确定的。所以代码执行的交错可能产生两个对象。
因此,怎么解决这个问题呢?如下代码
处理多线程问题
public class Singleton {
private static Singleton uniqueInstance;
privte Singleton() {}
pulbic static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
这个解决方案时正确的,不过,这个同步关键字只有在第一次执行这段代码时才会有作用,换句话说,不做使用当我们已经有这个唯一实例时,就不需要这个synchronize方法,在第一次之后,synchronization就成为了不必要的花销。
那怎么改善一下多线程问题?
- 当应用对我们获得实例的性能不挑剔的时候,我们就不用做任务处理。
- 在把lazily created 变成eagerly created。
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;
}
}
使用这个实现,我们依赖JVM当类加载的时候去创建唯一单例实例,在任何线程使用它的时候,JVM确保唯一实例的创建。
- 使用双重检查锁定去处理使用synchronization in getInstance();
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (this) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
当单例实例话的时候,volatile 关键字确保在多线程情况下正确地处理唯一实例变量。
当使用getinstance() 存在性能问题时,使用这个方法可以彻底地降低花销。不过这个方法不能用在java1.4或者之前的版本。
单例模式讲到这里差不多就结束了。
为什么不能仅仅创建类的时候全部方法或者变量定义成静态的?,这个根单例不一样吗?
因为静态的方式初始是在java中处理,这些会变的非常肮脏,尤其是设计多个类的时候。通常这个方案使问题变得微妙,很难发现问题,当涉及到初始化顺序。除非有操作需要你去用这种反式去实现单例,最好使停留在面相对象的世界。在java中,全局变量是基于静态的对象引用。有很多不利的因素使用全局变量这种方式。全局变量意在鼓励开发者去污染命名空间对一些简单的对象,而单例模式不鼓励去这样做。