单例模式--只有一个实例
2015-10-19 18:06
295 查看
什么是单例模式:
确保一个类只有一个实例,并提供一个全局访问点。
注意:使用起来类似静态方法,但是它不是静态方法,而是类。需要new关键字来实例化。
单例模式的基本使用方式可以概括为3个步骤:
1.创建一个private静态变量;
2.创建一个private空构造器;
3.创建一个public静态访问点,用来将唯一实例返回给外部调用者。
基本使用方式:
方法1:
Java代码
public class SingletonCommon {
private static SingletonCommon uniqueInstance;//步骤一
private SingletonCommon(){}//步骤二
public static SingletonCommon getInstance()//步骤三
{
if(uniqueInstance == null)
{
uniqueInstance = new SingletonCommon();
}
return uniqueInstance;
}
}
虽然看上去简单。但是这个类还存在问题。
我们的程序基本都是多线程的。就存在这样的可能:多个线程同时去调用这个实例,假设这个时候uniqueInstance == null,而线程A执行到if(uniqueInstance == null)这句语句,这时候系统线程调度,线程A交出cpu。转而执行B线程中的代码,而在B线程中也有可能访问这个类,那么它访问的时候,发现uniqueInstance == null,自然就创建一个实例。等到cpu的使用权回到A的时候,A从刚才停住的地方开始执行,也创建了一个实例!所以这个时候问题就出现了。
那么怎么解决这个问题呢?
方法:2:
使用synchronized关键字
通过这个关键字可以迫使线程在进入这个方法前,要等别的线程离开这个方法,这样就可以不会有两个线程同时访问这个类了,我们的问题就解决了。而且使用起来很简单,仅仅多了一个synchronized关键字而已。
Java代码
public class SingletonSynchronized {
private static SingletonSynchronized uniqueInstance;
private SingletonSynchronized(){}
public static synchronized SingletonSynchronized getInstance()
{
if(uniqueInstance == null)
{
uniqueInstance = new SingletonSynchronized();
}
return uniqueInstance;
}
}
但是这样也存在一个问题:效率。
每次调用getInstance()的时候都要进行同步,会造成负担。
那么怎么办才能提高效率呢?
方法3:
使用 双重检查加锁
利用双重检查加锁,首先检查是否已经创建了实例,如果还没有创建,才进行同步
这样一来就只有第一次会同步。这样就可以提高效率。
这里需要引入volatile关键字,这个关键字确保当uniqueInstance变量被初始化成SingletonDoubleLocking实例时,
多个线程正确地处理uniqueInstance变量。
Java代码
public class SingletonDoubleLocking {
private volatile static SingletonDoubleLocking uniqueInstance;
private SingletonDoubleLocking(){}
public static SingletonDoubleLocking getInstance()
{
synchronized (SingletonDoubleLocking.class)
{
if(uniqueInstance == null)
{
uniqueInstance = new SingletonDoubleLocking();
}
}
return uniqueInstance;
}
}
这样做似乎已经无懈可击了。但是还有问题存在!
问题是:双重检查加锁不适用于1.4及更早版本的Java
那还有别的方法吗?
我们知道前面的做法主要都是为了围绕着如何避免产生多个单例。
那能不能在调用getInstance()之前就生成实例呢?这样不就避免了所有的问题了吗?
方法4:
急切创建实例
在虚拟机加载这个
4000
类的时候就马上创建单例,这样就确保了在任何线程访问这个变量的时候,该实例都已经存在了。
Java代码
public class SingletonEagerly {
private static SingletonEagerly uniqueInstance = new SingletonEagerly();//虚拟机加载这个类的时候,这个类就已经被创建
private SingletonEagerly(){}
public static SingletonEagerly getInstance()
{
return uniqueInstance;//不需要判断是否为null,直接返回就可以了
}
}
总结:
方法1:最普通的单例,建议不要这样使用,以免出问题。
方法2:使用synchronized关键字,使得每个线程进入这个方法之前,要等候别的线程离开这个方法才进入。对程序性能要求不高的的情况下可以使用,仅仅比普通的单例模式多了一个关键字而已。
方法3:双重检查加锁法,使用volatile关键字,它确保了变量被初始化为实例时,多个线程能够正确地处理变量。这是个不错的选择,但是要注意不能用于java1.4及之前的java版本。在Android中是可以选择这样做的,因为一般我们选择的jdk都是1.5及以上的。
方法4:急切创建实例法,如果这个类的不大,负担不重,那么可以考虑直接在JVM加载的这个类的时候,直接就生成实例。
确保一个类只有一个实例,并提供一个全局访问点。
注意:使用起来类似静态方法,但是它不是静态方法,而是类。需要new关键字来实例化。
单例模式的基本使用方式可以概括为3个步骤:
1.创建一个private静态变量;
2.创建一个private空构造器;
3.创建一个public静态访问点,用来将唯一实例返回给外部调用者。
基本使用方式:
方法1:
Java代码
public class SingletonCommon {
private static SingletonCommon uniqueInstance;//步骤一
private SingletonCommon(){}//步骤二
public static SingletonCommon getInstance()//步骤三
{
if(uniqueInstance == null)
{
uniqueInstance = new SingletonCommon();
}
return uniqueInstance;
}
}
虽然看上去简单。但是这个类还存在问题。
我们的程序基本都是多线程的。就存在这样的可能:多个线程同时去调用这个实例,假设这个时候uniqueInstance == null,而线程A执行到if(uniqueInstance == null)这句语句,这时候系统线程调度,线程A交出cpu。转而执行B线程中的代码,而在B线程中也有可能访问这个类,那么它访问的时候,发现uniqueInstance == null,自然就创建一个实例。等到cpu的使用权回到A的时候,A从刚才停住的地方开始执行,也创建了一个实例!所以这个时候问题就出现了。
那么怎么解决这个问题呢?
方法:2:
使用synchronized关键字
通过这个关键字可以迫使线程在进入这个方法前,要等别的线程离开这个方法,这样就可以不会有两个线程同时访问这个类了,我们的问题就解决了。而且使用起来很简单,仅仅多了一个synchronized关键字而已。
Java代码
public class SingletonSynchronized {
private static SingletonSynchronized uniqueInstance;
private SingletonSynchronized(){}
public static synchronized SingletonSynchronized getInstance()
{
if(uniqueInstance == null)
{
uniqueInstance = new SingletonSynchronized();
}
return uniqueInstance;
}
}
但是这样也存在一个问题:效率。
每次调用getInstance()的时候都要进行同步,会造成负担。
那么怎么办才能提高效率呢?
方法3:
使用 双重检查加锁
利用双重检查加锁,首先检查是否已经创建了实例,如果还没有创建,才进行同步
这样一来就只有第一次会同步。这样就可以提高效率。
这里需要引入volatile关键字,这个关键字确保当uniqueInstance变量被初始化成SingletonDoubleLocking实例时,
多个线程正确地处理uniqueInstance变量。
Java代码
public class SingletonDoubleLocking {
private volatile static SingletonDoubleLocking uniqueInstance;
private SingletonDoubleLocking(){}
public static SingletonDoubleLocking getInstance()
{
synchronized (SingletonDoubleLocking.class)
{
if(uniqueInstance == null)
{
uniqueInstance = new SingletonDoubleLocking();
}
}
return uniqueInstance;
}
}
这样做似乎已经无懈可击了。但是还有问题存在!
问题是:双重检查加锁不适用于1.4及更早版本的Java
那还有别的方法吗?
我们知道前面的做法主要都是为了围绕着如何避免产生多个单例。
那能不能在调用getInstance()之前就生成实例呢?这样不就避免了所有的问题了吗?
方法4:
急切创建实例
在虚拟机加载这个
4000
类的时候就马上创建单例,这样就确保了在任何线程访问这个变量的时候,该实例都已经存在了。
Java代码
public class SingletonEagerly {
private static SingletonEagerly uniqueInstance = new SingletonEagerly();//虚拟机加载这个类的时候,这个类就已经被创建
private SingletonEagerly(){}
public static SingletonEagerly getInstance()
{
return uniqueInstance;//不需要判断是否为null,直接返回就可以了
}
}
总结:
方法1:最普通的单例,建议不要这样使用,以免出问题。
方法2:使用synchronized关键字,使得每个线程进入这个方法之前,要等候别的线程离开这个方法才进入。对程序性能要求不高的的情况下可以使用,仅仅比普通的单例模式多了一个关键字而已。
方法3:双重检查加锁法,使用volatile关键字,它确保了变量被初始化为实例时,多个线程能够正确地处理变量。这是个不错的选择,但是要注意不能用于java1.4及之前的java版本。在Android中是可以选择这样做的,因为一般我们选择的jdk都是1.5及以上的。
方法4:急切创建实例法,如果这个类的不大,负担不重,那么可以考虑直接在JVM加载的这个类的时候,直接就生成实例。
相关文章推荐
- 怎么样才能在初入职场风生水起
- Android:倍数提高工作效率的 Android Studio 奇技
- 使用jdk自带webservice发布webservice
- 一、gradle搭建
- CALayer Animation实践
- java中date和time的区别
- Java数组过滤
- OllyDBG完美教程(超强入门级)
- 布局 能够在代码中使用的布局
- iOS UITabBar的隐藏和显示
- EF6+SQLite3数据库出现类型转换失败的问题(指定的转换无效)
- 模拟登录新浪微博(Python) - 转
- How to display Computer,Home,Network,Trash and Mounted volumes icons on ubuntu 12.04 (Precise) deskt
- linux简单安装TOMCAT
- Codeforces 589G Hiring(BIT + 二分)
- 重入、降级锁
- random_shuffle源码
- Quartz2D绘图之利用路径绘制复杂图形
- jdk动态代理
- 【.NET深呼吸】Zip文件操作(2):动态生成Zip文档