前言
最近在审核自己以前的代码,看来下自己之前为了方便,很多小的功能都用单例模式来进行编写。因为已经距离现在有一定的时间了,有些东西没有考虑清楚,今天我就初级的讲一下单例的几个坑。
正文
我需要一个对象
当我们在使用一个引用对象的时候,我们需要把它实例化,然后才能使用,实例化的概念就是在内存上给你分配一块内存,然后根据对象的构造函数,往这块内存里写数据,最后把这块内存的引用返回给你。所以当我们实例化之后,我们就在内存上占用了一块内存。
我是个渣男
当我们这个对象在使用完了之后,我已经不再需要它了,那我就需要让它消失,把它所占用的内存给回归到内存池里去,可以让别的对象所用。但是我不想每次都要自己去管理让它被销毁。它能不能自觉点,我不需要的时候就去自我销毁,减少我的工作量呢?在现在很多高级语言里,他们的虚拟机都给我们集成了这项功能,它的名字就叫GC(垃圾回收)。当我需要一个对象的时候,我就向托管堆里去申请一块内存来使用,当我们不在使用的时候,我们把对这块内存的引用给释放了。然后通过root节点找到它引用对象的再引用,一直找到最后的引用节点(就是一层一层的递归寻找)把它们标记成被引用,然后当GC回收的时候,它们找到这块内存的标记是引用就会过滤掉,把那些不再被引用到的内存给回收掉了。正是因为在打标记的时候需要做递归寻找,这就是为啥会说GC有很大的消耗的原因,需要我们管理好引用对象的生成。
我觉得你很有用,你可以一直跟着我
当我们需要经常使用一个对象的时候,我不想经常性的实例化这个对象,我希望它能永远的存在。这个时候静态类就出现了,我们可以直接通过它这个类名去直接调用它里面的方法,它的所有方法和变量都应该做成静态的。当一个静态对象的出现,它就进化成root节点之一。将从它开始查找打标记。
你不要一开始就出现
当我们把一个类定义成静态类的时候,它里面的所有变量,属性,方法都将成为静态的,也就是说只要程序一启动,它就把它所需要的内存所占用了。有的时候,我们可能会在很晚的时候调用它,或者在这一次应用启动的时候都不会调用它里面的方法,那它在启动的时候就分配内存,就会造成内存泄漏了。这个时候我们就可以在在普通类里声明一个静态变量,它的类型就是这个类。给一个静态属性,当我们调用这个属性的时候才开始实例化这个变量,这个时候才开始分配内存,当它是一个静态属性的时候,它还是一个能永远存在内存中的root节点。
你是一个成熟的对象,用完就要放手
当我们生成了一个静态属性的时候,它是一个root节点,所以它所引用的引用也会一直在内存中,不会被释放。所以对象我们在使用完了之后要把它释放掉,我们就要定义好这种类里的方法都不需要缓存数据的。要不然内存爆炸的锅就要你来背一背了。
哦!同时出生了这么多个
当我们使用了多线程的时候,就会发生这样的事,这个对象被同时要求做好几件事,当第一次调用的时候,它就会同时生成好几份对象,因为在调用的时候它是没有实例化的,所以我们就会同时进行实例化,那它就同时会创建好几个对象。这个时候我们就要把这个方法设置为同步方法。让它在同一时间只能被一次调用。
你干活可真慢
当我们把这个获取对象的方法设置成同步方法的时候,它在同一时间只能被调用一次,当我们在多线程的时候,其实我们只是为了规避它被创建多次,这个时候,我们可以把它的同步方法放在第一次创建生成对象的时候,当对象已经存在了,那就算被同步使用,其实也不会对结果产生什么差异,毕竟我们在这里是尽量使用方法内部的变量 ,而不会去使用类内的局部变量。
总结
class Singlecase{
private static Singlecase instance = null;
private static object locker = null object();
public static Singlecase Instance{
get{
if(instance == null){
lock(locker){
if(instance == null){
instance = new Singlecase();
}
}
return instance;
}
}
}
}
上面的实例代码就是单例模式,单例模式在我的理解力就是为了能做一个全局的工具类,为了做一些通用的简单计算,不涉及一些数据的保存,只是为了一些数据处理。当我们使用单例模式的时候,需要保证我们这个类对一个不需要的对象,一直保持着引用,而使它的内存没有被回收掉。