最近看了一下关于锁的内容。感觉很多文章写的不是空洞就是细节繁琐。空洞,说明写的人水平有限;写的繁琐也不见的是好事,也可能说明作者没有吃透,云深不知处。有的作者从java讲到jvm的底层c++实现,翻代码虽不是人人都会,但是会的人也不少,列出源码未必就说明作者能做到云深也知处。我感觉有的作者也许就是在哗众取宠,大段代码贴上去,显示文章的高大上而已,我们读者千万别被坑了。
什么乐观锁,什么悲观锁,那都是好事者的意淫,很多学者要出论文,总得造点新东西出来吧。悲观锁,乐观锁,概念一出,岂不是一片立意新颖的论文啊。
我感觉在并发锁的发展史上,在那些造“锁”的技术人眼里,压根就没有什么乐观,什么悲观,人家就是为了提高性能而已,只是后人扣了很多的帽子而已,也许这些造“锁”人看到“并发”和“乐观”都一脸懵逼,这是啥玩意呢?
说一下个人的见解吧,时间有限,并未能详细查阅各种资料,如有疏忽请指正,不喜乱喷,只愿坦诚交流。在jdk5之前,锁只有一种,就是synchronized,用某个共享资源的时候先上锁,用完就释放,在jdk5之后,java出现了并发包,这些包里面的类都是拿到共享资源,直接就用,它压根就没有锁的概念和意识了,用:就是先read再write。
说到read和write,这里面也有说道,你看,拼写出read敲击了四次键盘,拼写出write敲击了五次键盘,在cpu里面也是一样,它都是分为好几步完成read或者write。后来,cpu发展了,read或者write可以一步完成,就跟你复制粘贴一样,一次就完成了。
在java的并发包里面,采用的都是read或者write一步完成的方式,整体的思路就是:

for(;;)
{
old = read();
result = write(old,new);
if (result)
{
break;
}
}

后来,好事者说这是乐观锁,至于是不是,我也不想论证,我在这里只是说了一下有锁和无锁。
无锁,就没有什么可说了,但是有锁,就有问题出现了。锁,刚开始是不可重入的,后来变成了可重入的。不可重入的锁,也很好模拟,我可以用redis模拟一下,此时的锁就是redis里面的key,锁可以是多种东西,这里采用redis key为了方便说明:

SomeBody{
doSomeThing()
{
redis.setnx(key);
doSomeThing();//再次调用doSomeThing方法,模拟重入
redis.del(key);
}
}//SomeBody End

SomeBody sb = new SomeBody();
Thread run ()
{
sb.doSomeThing();//卡住了,因为不可重入
}

下面说一下可重入锁:


SomeBody
{
@lock[this][threadId]//this表明lock的是此对象;threadId表明是被那个线程lock住了。
doSomeThing()
{
doSomeThing();
}
}//SomeBody End
SomeBody sb = new SomeBody();

Thread run ()
{
sb.doSomeThing();
}

此代码的解释如下:
sb.doSomeThing();和@lockthis两行代码的理解是:
(1)表明以sd为锁
(2)是Thread上的锁
这样的话,就知道是谁上的锁,那么谁就可以重入了。在这里大家要注意一下,对threadId的理解,其实线程thread在cpu里面就是一个数据结构,threadId就是来唯一标示这个数据结构的东西。

补充:
如果上面的例子不好理解,可以基于数据版本(version)的记录机制实现同样的效果。为数据增加一个版本标识,一般是通过为数据库表增加一个"version"字段,读取出数据时,将此版本号一同读出,之后更新时,对此版本号加1。此时,将提交数据的版本号与数据库表对应记录的当前版本号进行比对,如果提交的数据版本号大于数据库当前版本号,则予以更新,否则认为是过期数据。

标签: none

添加新评论