当我们在系统中需要频繁使用一个公用的类的时候,我们更多的希望不用每次在调用的时候去实例化一个新的对象。在设计模式当中有这样一种设计模式——单例模式。
单例模式可以简单的分为饥汉模式和懒汉模式,我们来分别看一下两种模式的实现方式吧:
饥汉模式:
public class PrintMessage {
private static PrintMessage pm = new PrintMessage();
/**
* 私有化构造函数
*/
private PrintMessage(){}
/**
* 获取全局唯一实例
* @return
*/
public synchronized static PrintMessage getInstance(){
return pm;
}
}
懒汉模式:
public class PrintMessage {
private static PrintMessage pm = null;
/**
* 私有化构造函数
*/
private PrintMessage(){}
/**
* 获取全局唯一实例
* @return
*/
public synchronized static PrintMessage getInstance(){
if(pm == null){
pm = new PrintMessage();
}
return pm;
}
}
两种模式可以同样实现在内存中生成唯一的对象,不过两者之间存在一定的区别,饥汉模式会在类装载的时候生成全局的唯一变量,而懒汉模式会在系统首次调用的时候生成全局唯一变量,一个是在系统初始化的时候耗费生成实例的时间,一个是在首次调用的时候耗费生成实例的时间。不过具体要看情况而选择不同的模式。
懒汉模式潜在中存在一定的危险性,当然这种危险性是相对的,就是假设在多个线程第一次同时调用该类,那么存在一种可能是生成多个对象,违反了单例的原则,不过这种可能性极低。我们有时候完全不必要考虑为了避免这一点安全性来给类加上更耗费资源的同步锁问题。
说到上面的同步,单例模式其实也存在一个多线程同步问题,因为单例的实现,目的就是在多线程调用情况下不过多的生成新的实力耗费资源。我们来看下面一段代码,代码用单例模式编写,顺序打印出一堆数据。
public class PrintMessage {
private static PrintMessage pm = null;
private int count = 0 ;
/**
* 私有化构造函数
*/
private PrintMessage(){}
/**
* 获取全局唯一实例
* @return
*/
public static PrintMessage getInstance(){
if(pm == null){
pm = new PrintMessage();
}
return pm;
}
/**
* 打印信息
*
*/
public void print() {
System.out.println(count);
/*try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
count++;
}
}
我们再模拟一个多线程去调用这个单例。看下输出结果:
public static void main(String[] args){
for (int i = 0; i < 50; i++) {
Thread t = new Thread(
new Runnable(){
public void run(){
PrintMessage.getInstance().print();
}
}
);
t.start();
}
}
我们可以很容易的看到打印出的结果有很多重复数据,我想大家应该可以看出来是典型的线程同步的问题。那么我们怎么避免这个问题呢?首先我想到再getInstance方法加上同步,同时只有一个对象获取该对象的实例。我们来看下下面的代码
public synchronized static PrintMessage getInstance(){
if(pm == null){
pm = new PrintMessage();
}
return pm;
}
继续执行多线程方法,我们发现结果依然不是我们希望的顺序递增。这是为什么呢?因为我们获取对象实例后,实际上已经获得了对象的一个映射,这是我们会释放该方法上的同步锁。所以依然没有对print方法起到同步的效果。我们来看下给print方法加上同步锁呢。
/**
* 打印信息
*
*/
public synchronized void print() {
System.out.println(count);
/*try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
count++;
}
继续执行先前的方法,怎么样,可以看到我们希望的顺序递增的效果了吧。当然不知道理解对不对,但是单例模式中的线程安全问题还是需要特别注意的。
分享到:
相关推荐
在Delphi中,常见的设计模式包括工厂模式、单例模式、观察者模式、策略模式等。例如,工厂模式提供了一种创建对象的抽象方式,使得代码不依赖具体的类实现;单例模式确保一个类只有一个实例,并提供全局访问点;观察...
1. **创建型模式** 主要关注对象的创建过程,如单例模式、工厂模式、抽象工厂模式、建造者模式和原型模式。这些模式提供了灵活的创建对象的方式,使得对象的创建过程可以被控制,同时也易于扩展和维护。 - 单例...
单例模式( Singleton ) 建造者模式( Builder ) 原型模式( Prototype ) 结构型模式包含了: 适配器模式( Adapter ) 装饰器模式( Decorator ) 代理模式( Proxy ) 外观模式( Facade ) 桥接模式( Bridge...
这份教程可能会从基础的单例模式开始,逐步介绍更复杂的模式,如装饰器、观察者和策略模式。每种模式都会解释其背后的意图、结构和行为,以及在Delphi中如何实现。此外,它可能还会包含实际项目中的应用示例,帮助...
- **创建型模式**(如单例模式、工厂模式):控制对象的创建过程,确保对象的唯一性和正确初始化。 - **结构型模式**(如适配器模式、装饰器模式):关注如何组合类和对象,以实现新的功能或改变已有类的行为。 -...
- **第1章**:设计模式初探,介绍设计模式的基本概念、历史背景以及其在iOS开发中的重要性。 - **第2章**:案例研究:设计一款应用,通过实际项目来展示设计模式的应用场景和实现步骤。 #### 第二部分:对象创建...
设计模式是对常见问题的解决方案,如单例模式、工厂模式等,它们可以帮助程序员更高效地解决实际问题。通过深入理解这些编程范式,可以提高代码的复用性和灵活性。 在实际的ACM竞赛中,团队协作和时间管理也是成功...
在实际开发中,我们还会用到一些设计模式,比如工厂模式(Factory Pattern)用于创建用户对象,单例模式(Singleton Pattern)确保登录系统的全局唯一性,策略模式(Strategy Pattern)用于处理不同类型的验证规则...
7. **程序设计模式**:设计模式是软件设计中的一种最佳实践,如工厂模式可用于创建用户对象,单例模式可以保证登录状态的唯一性。合理运用设计模式能使代码更易于理解和维护。 总的来说,C++成绩管理系统是一个很好...
7. **设计模式**:为了提高代码的可维护性和扩展性,开发者可能会运用到一些设计模式,如单例模式(管理音频资源)、工厂模式(创建播放器对象)等。 在MusicPlay的压缩包中,只有一个文件"MusicPlay",这可能是...
"MyPlayer"可能运用工厂模式创建播放器实例,单例模式确保播放器全局唯一,观察者模式用于更新UI状态等。 9. **编译与链接**:理解编译器如何将源码转换成可执行文件也非常重要。C++程序由多个源文件组成,通过预...
6. **设计模式**:为了保证代码的可维护性和扩展性,开发者可能会应用单例模式(管理服务器实例)、工厂模式(创建客户端连接)等设计模式。 7. **异常处理**:良好的异常处理机制能够确保程序在遇到错误时能优雅地...
此外,还可以考虑使用设计模式(如工厂模式、单例模式)来优化代码结构。 总的来说,这个项目提供了一个实际运用Python基础知识的机会,通过实践,你可以更好地理解和掌握Python的类、模块以及文件操作。尽管这是一...
10. **游戏框架设计**:理解"CocosTest"的整体架构和设计模式,如单例模式、观察者模式等,有助于我们构建自己的游戏框架。 总之,"Cocos2d-xGameTest"是一个很好的学习资源,通过解构和分析这个示例,可以全面了解...
它是Spring IoC容器的基础,提供了延迟加载和单例模式的支持。 - **特点**:BeanFactory是一个工厂模式的实现,它可以用来创建和管理Spring Bean。它支持依赖注入和生命周期管理,但通常被认为是低级别的API,因为...
本教程共分为5个部分,第一部分是C语言提高部分,第二部分为C++基础部分,第三部分为C++进阶部分,第四部分为C、C++及数据结构基础部分,第五部分为C_C++与设计模式基础,内容非常详细. 第一部分 C语言提高部分目录...
- **对象单态**:控制Bean实例的数量,例如单例模式。 - **初始化方法**:指定Bean实例化后的初始化逻辑。 - **销毁方法**:指定Bean销毁前的清理逻辑。 - **注入字段**:直接将依赖注入到字段。 - **注入属性**:...
`@Inject`用于声明依赖,`@Singleton`表示单例模式,确保一个类型在整个应用程序中只有一个实例。 5. **测试**:在Guice中,我们可以使用`Guice.createInjector()`创建一个测试用的Injector,然后在这个Injector中...