您的位置:首页 > 职场人生

让那些做面试官的屌丝lead不再抖脚系列(三)---同步异步探讨->lock

2015-03-10 18:00 218 查看
这次来讨论下lock,

首先为神马要lock?为了线程同步以及线程安全。

那神马是lock?

lock 关键字可确保当一个线程位于代码的临界区时,另一个线程不会进入该临界区。 如果其他线程尝试进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。

这是msdn上的解释,临界区是神马,其实就是同一个时候,只有一个线程可以访问的地方,换言之其实就是线程同步的区域,那lock的运作机理是神马呢?下面的解释很深奥,

对于任何一个对象来说,他在内存中的第一部分放置的是所有方法的地址,第二部分放着一个索引,他指向CLR中的SyncBlock Cache区域中的一个SyncBlock.什么意思呢?就是说,当你执行Monitor.Enter(Object)时,如果object的索引值为负数,就从SyncBlock Cache中选区一个SyncBlock,将其地址放在object的索引中。这样就完成了以object为标志的锁定,其他的线程想再次进行Monitor.Enter(object)操作,将获得object为正数的索引,然后就等待。直到索引变为负数,即线程使用Monitor.Exit(object)将索引变为负数。

至少我第一次看,根本没看懂,后几次看,也没怎么看懂,化成我自己的话,就是从SyncBlock里挑一个block标签,贴到这个线程上,说这已经被占用了,所以这个object的地址应该是和SyncBlock一样的,而不是空白的,也就是负数,当解锁后会变成空白的。那么问题来了,SyncBlock数组哪来的,CLR初始化时候就一起带来了,就是用来做lock不lock的事情的。

好了到这里我们需要补充的就是lock其实就是Monitor.Enter(Object),Monitor.Exit(),但是Monitor比起lock以外,还多了Monitor.Wait()/Purse()方法,用于将线程放入等待池,让出锁可以让别的进程访问这个代码块,而在别的线程中执行Purse()方法后,会把等待池里的线程放回线程队列,接下来可以继续访问。另外,lock是Monitor.Enter放入try catch后的结果。

单单一个概念就能扯出那么多,也真是麻烦了。

下面说几个重点:

1.lock(this)好嘛,为什么不好?

很简单的例子,我有个类A,A中有两个方法aa(),bb(),我在aa()中加了lock锁,然后现在有两个线程,线程1访问aa方法,线程2访问bb方法,当线程1先进入aa方法,线程1锁定了A对象,这时候你会发现,线程2访问不了bb方法,哪怕他们不是一个线程,原因就是因为他们的类被锁定了,因此一旦发生lock(this),一旦这个对象中发生锁定,那这个类中别的方法其实也被锁定了,无法进行访问,有可能会发生因lock(this)中死锁而所有线程无法继续的问题。

2.lock("aa")可以嘛,为什么不可以?

这个比较好理解,你可以把全世界的lock("aa")都看成同一把锁,这样只要一个地方出现lock("aa"),那全世界的的“aa”都会被锁。原因是因为字符串会被CLR暂留,暂留是什么?你问CLR去,我哪知道,同时typeof(Classa)也是这个问题,而且typeof的问题更大,会把Classa所有实例对象全部锁住,因为他们都是同一个类对象出来的。

3.lock()可以加值类型值么,为什么不可以?

如果理解了lock的概念,那这个就好理解了,因为值类型没法存储地址索引,SyncBlock标签没法被贴上,当然不行了。好的,可能这个答案不能让人信服,那换一个,如果出现lock(1),那其实就是把1进行装箱,变成一个引用类型,同时这是装箱的概念,但是每次装箱的结果都是不一样的,也就是每次装箱完了的1这个引用类型都是不同的,所以lock(1)其实锁的每次都是完全不同的东西,因此根本锁不住的。

4.lock()可以加public型么,为什么不可以?

当public对象被锁定,那可能出现别的地方的小朋友无法调用的危险,

5.稍微高端点,我lock一个System.MarshalByRefObject 的衍生对象可以么,就像个service,或者别的。

首先,MarshalByRefObject 是个神马,这在以后的讨论中我们会提及,有点AOP的概念,那事实上这个对象是个代理,自己并没有个实质,所以无法锁定的,哪怕你锁了,也是白搭。

6.lock(null)怎么样?

不行,为什么不行,会爆异常,但是为什么这个真不知道,这个之后可能会带来详细解释。

那lock哪些东西可以呢?

微软的解释是private的变量或者static private变量可以用来当锁,那其实除了我们上面提到的6种,也只剩这个了,所以还是挺好记的。

那来个搞脑子的问题,锁的到底是什么?是代码块,线程,还是对象?

答案就在lock()的括号中,如果之前的概念都理清了,那答案也就很明显了。

值得一提的是,List<>类中运用的锁其实是lock(this),因此才有了网上那么多同学去自己定义个锁重新用的结果。

好了,关于lock就那么多了,也是一个新的认识了,一般锁都会在线程或者单例模式中被问到,有个好处就是锁比较好懂,就像上面那些,你可以主动去用锁,让面试官来问,当然更淫荡的你可以故意不用锁,等着面试官来问,你觉得你写的有什么不好的地方可以改进呢?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: