何时创建单例?
何时创建的意思是,什么时候创建对象,这个维度决定了代码怎么写,以及是哪种(饿汉式/懒汉式)。
那具体是什么时候创建对象呢?一种是一开始就创建,一种是用的时候创建,一开始创建是指加载类的时候就创建,用的时候创建是指第一次使用的时候才创建。
一开始就创建的代码是:基于static。静态数据,就是一开始就创建。
第一次使用的时候才创建的代码是:基于if判断,如果还没有创建就创建,如果已经创建就使用旧的,即一直始终使用同一个对象。
代码如下
1、一开始就创建
public class IdGenerator {
private AtomicLong id = new AtomicLong(0);
private static final IdGenerator instance = new IdGenerator(); //基于static数据
private IdGenerator() {}
public static IdGenerator getInstance() {
return instance;
}
public long getId() {
return id.incrementAndGet();
}
}
2、第一次使用的时候才创建
public class IdGenerator {
private AtomicLong id = new AtomicLong(0);
private static IdGenerator instance; //也是static数据,但是是第一次用的时候才创建
private IdGenerator() {}
public static synchronized IdGenerator getInstance() {
if (instance == null) {
instance = new IdGenerator(); //第一次使用的时候才创建
}
return instance;
}
public long getId() {
return id.incrementAndGet();
}
}
刚才的代码性能有点低,因为除了第一次需要new对象需要加锁,后面都不需要加锁,本质其实是锁粒度太大了。所以为了优化性能,可以把锁换个位置,本质其实是优化锁的粒度,代码如下:
public class IdGenerator {
private AtomicLong id = new AtomicLong(0);
private static IdGenerator instance;
private IdGenerator() {}
public static IdGenerator getInstance() {
//核心是双重检查
if (instance == null) {
synchronized(IdGenerator.class) { // 此处为类级别的锁
if (instance == null) {
instance = new IdGenerator();
}
}
}
return instance;
}
public long getId() {
return id.incrementAndGet();
}
}
其实除了优化锁的粒度,另外一个核心就是双重检查,所以核心就是:双重检查 + 锁粒度。
最佳实践
虽然有两种时机创建对象,但是说实话,一般使用static就够了,即一开始创建就可以了,没有必要在用的时候去创建,一开始就创建是jdk就可以保证线程安全问题,用的时候才创建还要加同步关键字,还要自己思考和保证线程安全问题,一会儿双重检查的,一会儿这那的,把代码整的太复杂了,没必要。
应用场景
讲完理论,和代码实现,再来讲一下应用场景,应用场景就是:全局唯一。就这四个字。
接下来,你要做的,就是能分析什么情况下是全局唯一。
比如说,配置文件数据,解析为配置文件类之后,就只需要一个对象就可以了,因为就是只有一份数据。
然后,再比如说,写日志到磁盘文件,应用程序里肯定都是多线程写入的,但是写入的都是同一个磁盘文件,但是怎么做到不同线程写入日志到同一个磁盘文件的时候不乱序不乱套呢?这个时候就需要一个专门的中介,即日志框架来做这个事情了,底层实现原理其实就是单例,大家使用同一个单例往同一个磁盘文件写日志,这样就不会乱套了。
还有,id自增/id/分布式id,都是基于单例生成id,就是避免id乱掉。
八股文-spring bean scope
首先,有两个维度,一个维度是单例/多例,一个维度是java/web。
web是:当前请求/用户会话/全局即所有用户。
那spring bean默认是啥呢?单例。
最后一个问题:单例是线程安全吗?单例本身肯定是不安全的,但是由于项目里的控制器/service/dao一般都是无状态,所以没有线程安全问题!如果有数据,就有线程安全问题,因为单例的数据是共享数据。
参考:docs.spring.io/spring-fram…
cloud.tencent.com/developer/a…
参考
争哥:设计模式之美