同步操作将从 turnon/blog 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
title: 设计模式之单例模式
date: 2015-06-03 09:24:00
categories:
- 设计
- 设计模式
tags:
- 设计
- 设计模式
permalink: /pages/cf046f/
单例模式(Singleton)是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。
单例 (Singleton) 类声明了一个名为 getInstance
获取实例的静态方法来返回其所属类的一个相同实例。
单例的构造函数必须对客户端 (Client) 代码隐藏。 调用 getInstance
方法必须是获取单例对象的唯一方式。
所有单例的实现都包含以下两个相同的步骤:
new
运算符。如果你的代码能够访问单例类, 那它就能调用单例类的静态方法。 无论何时调用该方法, 它总是会返回相同的对象。
单例模式的优点:
单例模式的缺点:
请注意, 你可以随时调整限制并设定生成单例实例的数量, 只需修改 获取实例
方法, 即 getInstance
中的代码即可实现。
举例来说,一些资源管理器常常设计成单例模式。
在计算机系统中,需要管理的资源包括软件外部资源,譬如每台计算机可以有若干个打印机,但只能有一个 Printer Spooler, 以避免两个打印作业同时输出到打印机中。
每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。任务管理器中难以启动两个相同的 task。
getInstance
获取实例的静态方法来返回其所属类的一个相同实例。
获取实例
方法必须是获取单例对象的唯一方式。在本例中, 数据库连接类即是一个单例。
该类不提供公有构造函数, 因此获取该对象的唯一方式是调用 获取实例
方法。 该方法将缓存首次生成的对象, 并为所有后续调用返回该对象。
// 数据库类会对`getInstance(获取实例)`方法进行定义以让客户端在程序各处
// 都能访问相同的数据库连接实例。
class Database is
// 保存单例实例的成员变量必须被声明为静态类型。
private static field instance: Database
// 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构
// 造方法。
private constructor Database() is
// 部分初始化代码(例如到数据库服务器的实际连接)。
// ...
// 用于控制对单例实例的访问权限的静态方法。
public static method getInstance() is
if (Database.instance == null) then
acquireThreadLock() and then
// 确保在该线程等待解锁时,其他线程没有初始化该实例。
if (Database.instance == null) then
Database.instance = new Database()
return Database.instance
// 最后,任何单例都必须定义一些可在其实例上执行的业务逻辑。
public method query(sql) is
// 比如应用的所有数据库查询请求都需要通过该方法进行。因此,你可以
// 在这里添加限流或缓冲逻辑。
// ...
class Application is
method main() is
Database foo = Database.getInstance()
foo.query("SELECT ...")
// ...
Database bar = Database.getInstance()
bar.query("SELECT ...")
// 变量 `bar` 和 `foo` 中将包含同一个对象。
使用示例: 许多开发者将单例模式视为一种反模式。 因此它在 Java 代码中的使用频率正在逐步减少。
尽管如此, Java 核心程序库中仍有相当多的单例示例:
识别方法: 单例可以通过返回相同缓存对象的静态构建方法来识别。
数据库连接类即是一个单例。
该类不提供公有构造函数, 因此获取该对象的唯一方式是调用 获取实例
方法。 该方法将缓存首次生成的对象, 并为所有后续调用返回该对象。
// 数据库类会对`getInstance(获取实例)`方法进行定义以让客户端在程序各处
// 都能访问相同的数据库连接实例。
class Database is
// 保存单例实例的成员变量必须被声明为静态类型。
private static field instance: Database
// 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构
// 造方法。
private constructor Database() is
// 部分初始化代码(例如到数据库服务器的实际连接)。
// ...
// 用于控制对单例实例的访问权限的静态方法。
public static method getInstance() is
if (Database.instance == null) then
acquireThreadLock() and then
// 确保在该线程等待解锁时,其他线程没有初始化该实例。
if (Database.instance == null) then
Database.instance = new Database()
return Database.instance
// 最后,任何单例都必须定义一些可在其实例上执行的业务逻辑。
public method query(sql) is
// 比如应用的所有数据库查询请求都需要通过该方法进行。因此,你可以
// 在这里添加限流或缓冲逻辑。
// ...
class Application is
method main() is
Database foo = Database.getInstance()
foo.query("SELECT ...")
// ...
Database bar = Database.getInstance()
bar.query("SELECT ...")
// 变量 `bar` 和 `foo` 中将包含同一个对象。
懒汉式的实现思路是:你不找懒汉,懒汉根本就懒得去初始化自己。
instance
初始时没有初始化,只有当第一次调 getInstance()
时才创建实例。
缺点:当有两个线程调 getInstance()
方法,当它们同时执行到 if (null == instance)
这行代码,instance
为 null
。
继续向下执行,会生成两个实例,违背了单例模式的初衷。
public class LazySingleton {
private LazySingleton() {
System.out.println("Singleton()");
}
private static LazySingleton instance = null;
public static LazySingleton getInstance() {
if (null == instance) {
instance = new LazySingleton();
}
return instance;
}
}
懒汉式的实现思路是:饿汉根本等不及别人来找他,不管三七二十一先初始化了自身的实例,生怕自己饿着了。
类默认先直接初始化一个实例,以后调用 getInstance()
总是返回这个已创建好的实例。
缺点:在没有必要获取实例时,已经预先产生了开销。
优点:规避了懒汉式方法的线程问题,不用显示编写线程安全代码。
public class HungerSinleton {
private HungerSinleton() {
System.out.println("Singleton()");
}
private static HungerSinleton instance = new HungerSinleton();
public static HungerSinleton getInstance() {
return instance;
}
}
如果既不想在没有调用 getInstance(
) 方法时产生开销,又不想发生线程安全问题,就可以采用双重锁的形式。
public class SyncSingleton {
private SyncSingleton() {
System.out.println("Singleton()");
}
private static SyncSingleton instance = null;
public static SyncSingleton getInstance() {
if (null == instance) {
synchronized(SyncSingleton.class) {
if (null == instance) {
instance = new SyncSingleton();
}
}
}
return instance;
}
}
注:在外面判断了 instance 实例是否存在,为什么在锁定后又要在内部又判断一次?
这是因为,如果
instance
为null
时有两个线程同时调用getInstance()
,由于synchronized
机制,只允许一个线程进入,另一个需要等待。这时如果没有第二道
instance
是否为null
的判断,就可能发生第一个线程创建一个实例,而第二个线程又创建一个实例的情况。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。