同步比你想像的复杂。
事务更加。乐观锁与悲观锁相对比较简单。
同步的意思是,限制访问人数,而事务的意思是,大家一起来。我不怕人多!秋后算帐,你不提交就一切都白搭,你要提交我再看看有没有冲突,有冲突就回滚,没有冲突才提交。
乐观锁与悲观锁的范围比事务大。大多数锁都是悲观锁,乐观锁基本上意味着不锁,只在发现异常以后处理问题如回滚或抛出错误。
看看这个APACHE的事务性MAP包装器:
TransactionalMapWrapper | OptimisticMapWrapper | PessimisticMapWrapper | |
---|---|---|---|
Lost update | Possible | Not possible | Not possible |
Dirty write | Not possible | Not possible | Not possible |
Dirty read | Not possible | Not possible | Not possible |
Lost update | Possible | Not possible | Not possible |
Nonrepeatable read | Possible | Not possible | Not possible |
Phantoms | Possible | Not possible | Not possible |
Read Skew | Possible | Not possible | Not possible |
Write Skew | Possible | Possible | Not possible |
Readers block writers | No | No | Yes |
Writers block readers | No | No | Yes |
Writers block writers | No | No | Yes |
Might deadlock | No | No | Yes |
Commit might fail | No | Yes | No |
Isolation Level | Read Committed (with lost updates possible) | Snapshot (Oracle would call it Serializable) | Serializable |
事务有ACID四个基本要素。从中可以看出,悲观锁是全能的。因为悲观锁是真正序列化的。同步就是悲观锁。
但是同步与真正的悲观锁又不一样。真正的悲观锁其实很多是用在用户级别而不是程序及别的。比如把一条记录锁起来,谁也不让动直到我改完并且告诉程序这个可以动了。同步当然也可以有用户级别的意义,如果放在用户级别去讨论它的话,那么两者又完全是一样的语义了。
问题是,JAVA里面或其它任何语言里面,有synchronized语法的,它到底是什么意思?
有些人会以为只要synchronized了,数据就应该是安全的啦。数据的安全或者说完整性是一个说不清,道不完,无休无止的话题。比如你拿到这个数据并且打招呼让别人都不能动然后把它报告给了你的上级。但是你的上级还没有回复你,那你你就必须等待他的回复,在这之前你都不能决定释放这个数据的“锁”。你的上级又有同样的问题。如此以往,,,如果整个人类社会也跟计算机一样的话,恐怕这个世界早就死锁了。但是没有,为什么呢?因为人会忘记!人也会忽视,逻辑是死的,人却是活的。人是由氧气驱动的,而逻辑是由人驱动的。
这么说吧,synchronized 就是synchronized。它只synchronized它synchronized的地方,仅此而已 。也就是说,千万不要对它有太大的期望,它唯一的作用是也仅是保证被synchronized在同一个对象上的代码在同一时间只有一个能够得到运行,并且只能得到一份运行。比如一个方法被十个线程调用,但是同时只能有一个线程可以运行。运行的是线程,这个是基础。意思是说,synchronized 是一个完全机械的语义。它是一个非常低层次的语义在JAVA里面。
事务是一种服务。这种服务要求你不要考虑锁。它其实是一种锁机制的封装。不过这是向下的说法,向上它当然是提供一个“事务”服务。你了解这个就够了。其它的当然也有的就是事务的相关属性,隔离级别。你还要了解你的应用对隔离性的要求如何?然后才知道如何选择正确的隔离级别。
事务是最高层次的东西。锁是同步的一种捆绑到具体操作对象的运用。乐观锁连 同步都不是。它只是个最简单的数据版本检查。
看到这篇文章你可能会觉得事务或锁或同步很复杂,但是真正复杂的其实并不是这些而是事务的服务级别。要理解这个非常困难,这就是为什么很多人宁愿选择在多线程下苦捱日子而不愿意敲响事务的大门的原因。JAVA这么高雅到现在也没有提供任何事务方面的服务。倒是ANDROID在FRAGMENT里面用了一点事务,颇令人惊讶又不奇怪。因为ANDROID本来就是非常优秀的系统,如我前面写过的。ANDROID的语义能力只在JAVA的上面,ANDROID玩概念的本事也是一流的,比起JAVA只有过之无不及。有人,你?能超越一下ANDROID,你就是将制造一个新“神”!
如前所述,最大的问题是同步。锁跟同步的唯一区别或许是,同步是锁机制中其途径与目标的分离。因为锁一般都有具体的封锁目标,但是同步其实并不直接锁任何东西或者说对任何东西的操作,或者说它锁的东西就跟东西无关,它锁的是操作本身。所以说同步在这四个东西中的语义层次最低:它只是简单地“锁”,你可以用不一样的锁,但锁住的东西永远都是不确定的。
同步->锁->事务。
同步还有一个问题就是层次的问题,意思是你在哪里同步。比如a().b().c().d()。其中d()方法操作了一个数据,你到底是在a, b, c, d中的哪一个去同步它呢? 或者说d已经没有同步并且我修改不了这个代码,那么又该怎么办?一般来讲,应该圈定第一层直接访问的地方进行同步,这样的同步代价最小并且更安全。因为你锁定的东西越多事情就会变得越复杂。并且嵌套的机会也会越多。因为如果你不同步你的类,那么人家就可能必须同步你的类,这样的话死锁的机会 就开始浮现出来:同步你 这个人,他可能同时还在同步别人的东西,这样嵌套的机会就开始跑出来,所以死锁的机会自然就跟着出来了。但是如果你已经完整地处理了你的同步或者说线程安全,那么人家就没有必要这么干,自然也就减少了因为你的类而引起死锁的机会 。所以,所有的类都尽量在自己的范围内处理好同步。
试想,假如所有的类都同步好了自己,那么所有的同步操作都在自己的“本地”得到了执行,那么死锁的机会又将从哪里来呢?甚至连 同步的代码都不会有!