单例模式是一种设计模式,用于确保一个类仅有一个实例,并提供全局访问点。该模式适用于需要全局唯一实例的场景,如数据库连接、日志记录器等。实现单例模式的方法有多种,下面分别介绍两种常用的实现方式。
一、饿汉式
饿汉式是指在类加载时就创建唯一实例,因此在调用该类的方法前就已经存在了一个实例。这种实现方式简单易懂,但在某些场景下可能会造成资源浪费,因为实例可能永远不会被用到。
1、场景设计
实现场景:用单例模式实现一个简单的加法计算
2、C++实现
静态变量 instance_ 在程序启动时就已经被创建,因此不需要使用互斥锁来保证线程安全。由于该实现没有懒加载,因此可能会浪费一些资源,但是可以确保线程安全。
#include <iostream>
using namespace std;
class Singleton {
private:
double operA;
double operB;
long result;
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton* getInstance();
void setOperA(double value);
void setOperB(double value);
long getResult();
};
//成员函数定义
void Singleton::setOperA(double value){
operA = value;
}
void Singleton::setOperB(double value){
operB = value;
}
long Singleton::getResult(){
result = operA + operB;
return result;
}
Singleton* Singleton::getInstance(){
static Singleton instance_;
return &instance_;
}
int main()
{
Singleton::getInstance()->setOperA(123);
Singleton::getInstance()->setOperB(456);
cout << Singleton::getInstance()->getResult() << endl;
return 0;
}
3、Java实现
Singleton.java
package creatingpattern.singleton;
public class Singleton {
/** 操作数A */
int operA;
/** 操作数B */
int operB;
/** 计算结果 */
long result;
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
public void setOperA(int operA) {
this.operA = operA;
}
public void setOperB(int operB) {
this.operB = operB;
}
public long getResult() {
result = operA + operB;
return result;
}
}
SingletonDemo.java
package creatingpattern.singleton;
public class SingletonDemo {
public static void main(String args[]) {
Singleton.getInstance().setOperA(123);
Singleton.getInstance().setOperB(456);
System.out.println("result = " + Singleton.getInstance().getResult());
}
}
二、懒汉式
懒汉式是指在第一次调用类的方法时才创建唯一实例,这种实现方式可以避免资源浪费,但需要在多线程环境下考虑线程安全问题。
1、场景设计
实现场景:用单例模式实现一个简单的加法计算
2、C++实现
在懒汉式单例模式中,第一次调用 getInstance() 时才创建实例,如果有多个线程同时调用该函数,可能会同时创建多个实例,因此需要使用互斥锁来保证同步。在 instance_ 为空时,只有一个线程能够获取锁,其他线程需要等待直到锁被释放。注意,以下代码在Linux下使用g++进行编译可正常运行,在其他编译环境下有可能无法编译通过,比如QT环境下,因为MinGW尚未实现线程库。
#include <iostream>
#include <thread>
#include <mutex>
#include <unistd.h>
using namespace std;
class Singleton {
private:
static mutex mutex_;
static Singleton* instance_;
double operA;
double operB;
long result;
public:
static Singleton* getInstance() {
if (instance_ == NULL) {
lock_guard<mutex> lock(mutex_);
if (instance_ == NULL) {
instance_ = new Singleton();
}
}
return instance_;
}
void setOperA(double value);
void setOperB(double value);
long getResult();
private:
Singleton() = default;
~Singleton() = default;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
mutex Singleton::mutex_;
Singleton* Singleton::instance_ = NULL;
//成员函数定义
void Singleton::setOperA(double value){
operA = value;
}
void Singleton::setOperB(double value){
operB = value;
}
long Singleton::getResult(){
result = operA + operB;
return result;
}
int main()
{
Singleton::getInstance()->setOperA(123);
Singleton::getInstance()->setOperB(456);
cout << Singleton::getInstance()->getResult() << endl;
return 0;
}
3、Java实现
注意:在多线程环境下,可能会出现多个线程同时进入 if (instance == null) 的情况,导致创建多个实例。为了避免这种情况,可以采用双重检查锁定或者静态内部类的方式实现懒汉式单例模式。
Singleton.java
package creatingpattern.singleton;
public class Singleton {
private static volatile Singleton instance;
/** 操作数A */
int operA;
/** 操作数B */
int operB;
/** 计算结果 */
long result;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
public void setOperA(int operA) {
this.operA = operA;
}
public void setOperB(int operB) {
this.operB = operB;
}
public long getResult() {
result = operA + operB;
return result;
}
}
SingletonDemo.java代码与饿汉式代码相同。