单例设计模式 (Singleton Design Pattern) 理解起来非常简单.一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式.简称单例模式.
实战案例一: 处理资源访问冲突
一个例子,自定义实现了一个往文件中打印日志的Logger类.具体实现代码如下:
点击查看代码
public class Logger {
private FileWriter writer;
public Logger() {
File file = new File("/Users/wangzheng/log.txt");
writer = new FileWriter(file, true); //true表示追加写入
}
public void log(String message) {
writer.write(mesasge);
}
}
// Logger类的应用示例:
public class UserController {
private Logger logger = new Logger();
public void login(String username, String password) {
// ...省略业务逻辑代码...
logger.log(username + " logined!");
}
}
public class OrderController {
private Logger logger = new Logger();
public void create(OrderVo order) {
// ...省略业务逻辑代码...
logger.log("Created an order: " + order.toString());
}
}
在上面的代码中,我们注意到,所有的日志都写入到同一个文件/Users/wangzheng/log.txt中。在UserController和OrderController中,我们分别创建两个Logger对象。在Web容器的Servlet多线程环境下,如果两个Servlet线程同时分别执行login()和create()两个函数,并且同时写日志到log.txt文件中,那就有可能存在日志信息互相覆盖的情况。
为什么会出现互相覆盖呢?我们可以这么类比着理解。在多线程环境下,如果两个线程同时给同一个共享变量加1,因为共享变量是竞争资源,所以,共享变量最后的结果有可能并不是加了2,而是只加了1。同理,这里的log.txt文件也是竞争资源,两个线程同时往里面写数据,就有可能存在互相覆盖的情况。
我们将Logger设计成一个单例类,程序中只允许创建一个Logger对象,所有的线程共享使用的这一个Logger对象,共享一个FileWriter对象,而FileWriter本身是对象级别线程安全的,也就避免了多线程情况下写日志会互相覆盖的问题。
按照这个设计思路,我们实现了Logger单例类,具体代码如下:
点击查看代码
public class Logger {
private FileWriter writer;
private static final Logger instance = new Logger();
private Logger() {
File file = new File("/Users/wangzheng/log.txt");
writer = new FileWriter(file, true); //true表示追加写入
}
public static Logger getInstance() {
return instance;
}
public void log(String message) {
writer.write(mesasge);
}
}
// Logger类的应用示例:
public class UserController {
public void login(String username, String password) {
// ...省略业务逻辑代码...
Logger.getInstance().log(username + " logined!");
}
}
public class OrderController {
public void create(OrderVo order) {
// ...省略业务逻辑代码...
Logger.getInstance().log("Created a order: " + order.toString());
}
}