单例模式的使⽤场景
就算你没有⽤到过其他的设计模式,但是单例模式你肯定接触过,⽐如,Spring 中 bean 默认就是单例模式的,所有⽤到这个 bean 的实例其实都是同⼀个。
单例模式的使⽤场景
什么是单例模式呢,单例模式(Singleton)⼜叫单态模式,它出现⽬的是为了保证⼀个类在系统中只有⼀个实例,并提供⼀个访问它的全局访问点。从这点可以看出,单例模式的出现是为了可以保证系统中⼀个类只有⼀个实例⽽且该实例⼜易于外界访问,从⽽⽅便对实例个数的控制并节约系统资源⽽出现的解决⽅案。
使⽤单例模式当然是有原因,有好处的了。在下⾯⼏个场景中适合使⽤单例模式:
1、有频繁实例化然后销毁的情况,也就是频繁的 new 对象,可以考虑单例模式;
2、创建对象时耗时过多或者耗资源过多,但⼜经常⽤到的对象;
3、频繁访问 IO 资源的对象,例如数据库连接池或访问本地⽂件;
下⾯举⼏个例⼦来说明⼀下:
1、⽹站在线⼈数统计;
其实就是全局计数器,也就是说所有⽤户在相同的时刻获取到的在线⼈数数量都是⼀致的。要实现这个需求,计数器就要全局唯⼀,也就正好可以⽤单例模式来实现。当然这⾥不包括分布式场景,因为计数是存在内存中的,并且还要保证线程安全。下⾯代码是⼀个简单的计数器实现。
public class Counter{
private static class CounterHolder{
private static final Counter counter = new Counter();
}
private Counter(){
System.out.println("");
}
public static final Counter getInstance(){
unter;
}
private AtomicLong online = new AtomicLong();
单例模式的几种实现方式
public long getOnline(){
();
}
public long add(){
return online.incrementAndGet();
}
}
2、配置⽂件访问类;
项⽬中经常需要⼀些环境相关的配置⽂件,⽐如短信通知相关的、邮件相关的。⽐如 properties ⽂件,这⾥就以读取⼀个properties ⽂件配置为例,如果你使⽤的 Spring ,可以⽤ @PropertySource 注解实现,默认就是单例模式。如果不⽤单例的话,每次都要 new 对象,每次都要重新读⼀遍配置⽂件,很影响性能,如果⽤单例模式,则只需要读取⼀遍就好了。以下是⽂件访问单例类简单实现:
public class SingleProperty{
private static Properties prop;
private static class SinglePropertyHolder{
private static final SingleProperty singleProperty = new SingleProperty();
}
/**
* config.properties 内容是 test.name=kite
*/
private SingleProperty(){
System.out.println("构造函数执⾏");
prop = new Properties();
InputStream stream = ClassLoader()
.getResourceAsStream("config.properties");
try {
prop.load(new InputStreamReader(stream, "utf-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static SingleProperty getInstance(){
return SinglePropertyHolder.singleProperty;
}
public String getName(){
("test.name").toString();
}
public static void main(String[] args){
SingleProperty singleProperty = Instance();
System.out.Name());
}
}
3、数据库连接池的实现,也包括线程池。为什么要做池化,是因为新建连接很耗时,如果每次新任务来了,都新建连接,那对性能的影响实在太⼤。所以⼀般的做法是在⼀个应⽤内维护⼀个连接池,这样当任务进来时,如果有空闲连接,可以直接拿来⽤,省去了初始化的开销。所以⽤单例模式,正好可以实现⼀个应⽤内只有⼀个线程池的存在,所有需要连接的任务,都要从这个连接池来获取连接。如果不使⽤单例,那么应⽤内就会出现多个连接池,那也就没什么意义了。如果你使⽤ Spring 的话,并集成了例如 druid 或者 c3p0 ,这些成熟开源的数据库连接池,⼀般也都是默认以单例模式实现的。
单例模式的实现⽅法
如果你在书上或者⽹站上搜索单例模式的实现,⼀般都会介绍5、6中⽅式,其中有⼀些随着 Java 版本的升⾼,以及多线程技术的使⽤变得不那么实⽤了,这⾥就介绍两种即⾼效,⽽且⼜是线程安全的⽅式。
1. 静态内部类⽅式
public class Singleton{
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (www.233077/){wwwzhaotai}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种写法仍然使⽤ JVM 本⾝机制保证了线程安全问题,由于 SingletonHolder 是私有的,除了 getInstance() ⽅法外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进⾏同步,没有性能缺陷;也不依赖 JDK 版本。上⾯的两个例⼦就是⽤这种⽅式实现的。
2. 枚举⽅式
public enum SingleEnum {
INSTANCE;
SingleEnum(){
System.out.println("构造函数执⾏");
}
public String getName(www.yigouyule2){
return"singleEnum";
}
public static void main(String[] args){
SingleEnum singleEnum = SingleEnum.INSTANCE;
System.out.Name());
}
}
我们可以通过 SingleEnum.INSTANCE 来访问实例。⽽且创建枚举默认就是线程安全的,并且还能防⽌反序列化导致重新创建新的对象。静态块
什么是静态块呢。
1、它是随着类的加载⽽执⾏,只执⾏⼀次,并优先于主函数。具体说,静态代码块是由类调⽤的。类调⽤时,先执⾏静态代码块,然后才执⾏主函数的;
2、静态代码块其实就是给类初始化的,⽽构造代码块是给对象初始化的;
3、静态代码块中的变量是局部变量,与普通函数中的局部变量性质没有区别;
4、⼀个类中可以有多个静态代码块;
他的写法是这样的:
static {
System.out.println("static executed");
}
来看⼀下下⾯这个完整的实例:
public class SingleStatic{
static {
System.out.println("static 块执⾏中...");
}
{
System.out.println("构造代码块执⾏中...");
}
public SingleStatic(www.120xh ){
System.out.println("构造函数执⾏中");
}
public static void main(String[] args){
System.out.println(www.huachengj1980/"main 函数执⾏中");
SingleStatic singleStatic = new SingleStatic();
}
}
他的执⾏结果是这样的:
static 块执⾏中...
main 函数执⾏中
构造代码块执⾏中...
构造函数执⾏中
从中可以看出他们的执⾏顺序分别为:
1、静态代码块
2、main 函数
3、构造代码块
4、构造函数
利⽤静态代码块只在类加载的时候执⾏,并且只执⾏⼀次这个特性,也可以⽤来实现单例模式,但是不是懒加载,也就是说每次类加载就会主动触发实例化。
除此之外,不考虑单例的情况,利⽤静态代码块的这个特性,可以实现其他的⼀些功能,例如上⾯提到的配置⽂件加载的功能,可以在类加载的时候就读取配置⽂件的内容,相当于⼀个预加载的功能,在使⽤的时候可以直接拿来就⽤。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。