1:什么是单例设计模式
单例设计模式(Singleton Design Pattern)理解起来非常简单。一个类只允许创建一个对象(或者实例),
那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。
2:单例模式的应用场景
处理资源访问冲突
在 UserController 和 OrderController 中,
我们分别创建两个 Logger 对象。在 Web 容器的 Servlet 多线程环境下,
如果两个 Servlet 线程同时分别执行 login() 和 create() 两个函数,并且同时写日志到 log.txt 文件中,那就有可能存在日志信息互相覆盖的情况。
@Slf4j
public class Logger {
private FileWriter writer;
public Logger() throws IOException {
File file = new File("A:\\log.txt");
//true表示追加写入
writer = new FileWriter(file, true);
}
public void log(String message) throws IOException {
log.info("写入数据:{}",message);
writer.write(message);
writer.write("\n");
writer.close();
}
}
public class UserController {
private Logger logger = new Logger();
public UserController() throws IOException {
}
public void login() throws IOException {
// ...省略业务逻辑代码...
logger.log("HSJ User logined!");
}
}
public class OrderController {
private Logger logger = new Logger();
public OrderController() throws IOException {
}
public void create() throws IOException {
// ...省略业务逻辑代码...
logger.log("Created an order");
}
}
3:尝试添加 synchronized 对象级别 锁解决
这种锁是一个对象级别的锁,一个对象在不同的线程下同时调用 log() 函数,会被强制要求顺序执行。
不同的对象之间并不共享同一把锁。
在不同的线程下,通过不同的对象调用执行 log() 函数,锁并不会起作用,
@Slf4j
public class Logger {
private FileWriter writer;
public Logger() throws IOException {
File file = new File("A:\\log.txt");
//true表示追加写入
writer = new FileWriter(file, true);
}
public void log(String message) throws IOException {
log.info("写入数据:{}",message);
//添加this 为对象级别的锁 锁无效
synchronized (this) {
writer.write(message);
}
writer.write("\n");
writer.close();
}
}
4:尝试添加 synchronized 类级别 锁解决
@Slf4j
public class Logger {
private FileWriter writer;
public Logger() throws IOException {
File file = new File("A:\\log.txt");
//true表示追加写入
writer = new FileWriter(file, true);
}
public void log(String message) throws IOException {
log.info("写入数据:{}",message);
//添加Logger.class 为类级别的锁 锁生效
synchronized (Logger.class) {
writer.write(message);
}
writer.write("\n");
writer.close();
}
}
5:使用单例设计模式解决
我们将 Logger 设计成一个单例类,程序中只允许创建一个 Logger 对象,
所有的线程共享使用的这一个 Logger 对象,共享一个 FileWriter 对象,
同时FileWriter 本身是对象级别线程安全的,也就避免了多线程情况下写日志会互相覆盖的问题。
执行顺序:
//类加载仅执行一次
//1:静态常量在类加载时初始化 调用方法new LoggerSingleton()
//2:被步骤1调用 执行new LoggerSingleton()
//3:在原本方法执行 new LoggerSingleton();
//注意此处final修饰仅表示 LoggerSingleton instance 不可变
1:private static final LoggerSingleton instance = new LoggerSingleton();
2:public LoggerSingleton() {}
3:public LoggerSingleton() {}
//由于getInstance添加了static修饰 因此这里是直接调用getInstance方法
4: public static LoggerSingleton getInstance() { return instance; }
5: public static LoggerSingleton getInstance() { return instance; }
@Slf4j
public class LoggerSingleton {
//static 静态变量 在类加载时初始化
//final 变量变常量 只能被赋值一次 在类加载时初始化
private static final LoggerSingleton instance = new LoggerSingleton();
public static LoggerSingleton getInstance() { return instance; }
private FileWriter writer;
public LoggerSingleton() {
File file = new File("A:\\log.txt");
//true表示追加写入
try {
writer = new FileWriter(file, true);
} catch (IOException e) {
e.printStackTrace();
}
}
public void log(String message) throws IOException {
log.info("写入数据:{}", message);
writer.write(message);
writer.write("\n");
writer.close();
}
}
public class LoggerSingletonDemoStart {
public static void main(String[] args) throws IOException {
//如果没有该 new LoggerSingleton(); 则上面第三步去掉 其他一致
new LoggerSingleton();
//这样就只能获取该单例的Logger对象
LoggerSingleton.getInstance().log("我是测试单例的Logger啊");
LoggerSingleton.getInstance().log("我是测试单例的Logger啊");
}
}
项目连接
请配合项目代码食用效果更佳:
项目地址:
https://github.com/hesuijin/hesujin-design-pattern
Git下载地址:
https://github.com.cnpmjs.org/hesuijin/hesujin-design-pattern.git
demo-study模块 下 build_design_pattern singleton包下 loggerDemo包
网友评论