大话Java世界里的锁
博客专区 > Bieber 的博客 > 博客详情
大话Java世界里的锁
Bieber 发表于3年前
大话Java世界里的锁
  • 发表于 3年前
  • 阅读 127
  • 收藏 2
  • 点赞 0
  • 评论 0

标题:腾讯云 新注册用户域名抢购1元起>>>   

摘要: Java对余锁方面提供的API有很多,我这里将以我的开发经验,在各个阶段对Java锁的接触来解释对锁的认识。内容略“很黄很暴力”,纯清妹妹慎入!

#大话Java世界里的锁# Java对余锁方面提供的API有很多,我这里将以我的开发经验,在各个阶段对Java锁的接触来解释对锁的认识。内容略“很黄很暴力”,纯清妹妹慎入! ##JVM感知的锁## 在接触Thread的时候,好像打包一样,买一送一的送给了我一个synchronized。当时并不知道它是拿来干嘛的,只是知道在方法上面加一个这个,这方法就很安全,貌似和我的肩膀一样厚实安全。我们经常会写下面这样的代码:

<!--lang:java-->
public synchronized void safeMethod(){
//do something	
}

但是这个时候synchronized是做了什么申请,当时的我并不知道。随着码代码的日子越来越多,我知道了这其实是加锁了。“锁”是一个动词,锁什么呢?为了知道锁什么,不知道又过了多少个码代码的日夜,有一天灵光一闪,终于恍然大悟。其实这个锁什么很重要,如果不知道到底锁了什么那么今天的安全,可能是日后的一个坑。那么我们来看看到底锁了什么吧。

其实不知道锁什么,是一种对锁的错误使用,因为知道说什么,你就知道那里需要同步。上面的代码锁的对象其实是safeMethod方法,那么所有线程到safeMethod方法这里,就会进行一次列好队,各自拿上号码牌,有序的进入safeMethod方法,并且只能有一个线程进去,不然这个方法就要“挤爆”了。这里说的的锁是锁住某个方法,可不可以锁住其他的东西呢?针对synchronized还可以下面方式的使用:

<!--lang:java-->
public void lockBlock(){
	synchronized{
	
	}
}
public void lockField(){
	synchronized(fieldVar){

	}
}
public void lockObject(){
	synchronized(this){
	
	}
}
public void lockClass(){
	synchronized(this.getClass()){
	
    }	
}

上面基础场景,它们锁住的范围一次在增大,第一个只锁住一段代码块,第二个只锁住一个变量,第三个锁住的是当前对象,第四个是锁住了类。通过无数个失眠的深夜,欣赏完岛国杂技之后的总结中发现,只要是锁住了什么东西,假如这个锁别其他线程占有了,就必须要等待其他线程的释放,你才能去占有这个锁。并且锁住的某个变量或者对象实体(类也是一个对象实体),那么对其的所有操作都串行化了(就是需要排队)。比如锁住的是一个Class实体,那么对该类的所有操作都会串行化,所以锁住类的影响范围是最大的。到现在我都不知道解释清楚了没。还是不看杂技了,专心写完再看。

为了更好的理解JVM里面锁,我们来看一段对话吧,设计三个角色一个是JVM(很威严,老大范),一个是线程(小罗罗),另一个是User对象妹妹

在看肥皂剧之前,先看看事情发展的起因。JVM有一个仓库,仓库里面存放了很多对象,线程经常来JVM这里租借对象来玩玩(玩坏了不负责)。于是某一天,线程又来JVM这里租借一个对象玩玩,故事边这样开始了:

线程JVM大人,我想找User对象玩玩(表情很猥琐,声音很淫荡)<br> JVM:你这贱奴,每天来找User妹妹,她都被你玩坏了(声音洪亮,表情庄严)<br> 线程:大人,我知道User妹妹很受欢迎,不知道她现在是否方便呀?<br> JVM:让我问问User妹妹<br> JVMUser,一位线程想来和你玩耍,问你是否方便<br>

剧情到这里需要跳出来一下,这里有两种场景,场景1:

User:我正在陪另一个哥哥玩耍呢,让他等会在进来。<br> JVM:好嘞,那妹妹好好陪着这位玩耍。<br> JVM:(对着线程说)你来的不巧,User妹妹正在陪着另一个高富帅呢,你等会吧。<br> 线程:好吧,我就在这里候着,看谁敢在我之前和User妹妹玩耍。

场景2:

User:哦?这里刚好有一位哥哥在和我玩呢。又来了一个,那不是玩的更Hight?速叫那位哥哥进来。<br> JVM:小心点,我昨天刚给你换的床。<br> JVM:(对着线程说)你进去吧,User妹妹呼唤你。<br> 线程:好嘞,User妹妹!我来了!<br> ....于是User妹妹和她的哥哥们快乐的玩耍着。<br>

上面两种场景可以映射到加了锁和没加锁的情况,不知道是否把剧情描述清楚了。这里的User妹妹代表的是锁的范围:方法(在JVM里面方法也是通过一个类来定义的),字段,类,对象。到此,我知道了锁,以及锁所涉及的哪些方面。锁的一个重要的方面就是范围,如何很好的控制锁的范围,才是锁的一个关键。

这种锁的同步方式是JVM提供给我的一种在并发情况下如何让代码更加的有安全感,由于是JVM出的,所以对其的信任度也比较高。官方出的东西,大家都知道,很稳定,但是性能上面还是有一定的瓶颈,虽然后面官方对这方面也有改进,但也是看到有别人家的东西之后才慢慢的去改进,就像EJB在Spring的逼迫下不断的改进一样。

##JVM无感知的锁## 从出生第一天我就知道了,条条大路通岛国的真理,于是我一直在寻求有没有其他途径来通向我心中的岛国(除了整天翻过社会主义的墙)。真理那就一定是对于任何事情进行抽象之后都可以复用的,这个和好的软件设计是一样的。既然JVM大人提供了一个很官方的实现,同时也公布了他的伟大思想,那就有NB的人来实现一个更好的(就像Rod johnson实现了他的Spring一样),这个NB的人就是我爱慕已久的Doug Lea,他通过使用JAVA实现了一个锁机制,让开发能够真正的感受到锁的存在,而JVM提供的锁是开发人员基本上感觉不到锁在哪里,需要YY一下才能知道它的存在。Doug lea提供锁的方式一般是如下形式:

<!--lang:java-->
Lock lock = new ReentrantLock();
public void doSomething(){
	try{
		lock.lock();
		//safe block
	}finally{
		lock.unlock();
	}
}

一看类名就知道是一把锁,不像JVM那么委婉。这种方式,需要先常见一把锁对象,这里可以理解为是一个锁的管理器,而lock.lock()才是去申请一把锁,lock.unlock()则是释放锁。不管是类的定义和方法名的定义都是如此的直白,不需要费脑细胞都知道怎么用,并且也很清楚的看到锁住了什么,即lock.lock()lock.unlock()中间区域。下面再看一段对话看看lock.lock()lock.unlock()做了什么?剧情设计到著名打杂演员线程,新秀lock(ReentrantLock对象)。

lock是一个厕所管理员,线程每天都会去那里解大便。故事是发生在一个早上,线程和往常一样去lock管理的测试解大便,发现外面没人,心里想可以顺利解决了!<br> 线程lock!开门,我要解大便,快不行了,拉裤子上了!<br> lock:急什么呀,排队,这里面有人解大便呢,你等着!<br> 线程:能不能告诉里面哥们快点呀?<br> lock:你就在外面候着,他出来了你才能进去!<br> 线程就这样一直等着,可是今天早上来解大便的人特别多,外面等的人多了起来。此时里面解大便的人出来了!线程喜出望外!

接下来又有两个场景

场景1:

lock:现在外面可以进来一个人了!<br> lock这话落音,外面等着解大便的人争先恐后的挤进厕所,线程虽然第一个来,但是被这人群挤哪去了都不知道!于是线程没挤进去,继续在外面候着!<br> 这样不知道过了多少回合线程都没挤进厕所<br> 就这样线程被shi给憋死了!<br>

场景2:

lock线程你进去吧,谁叫你第一个来!再过会,估计你就拉在我厕所门口了!后面的排好队,不要插队了!<br> 线程就这样顺利的解决了,轻松的出来了!从此过上了幸福的生活!

不知道剧情是否有岛国杂技那么精彩。这里来进行一下剧评,这里的厕所可以理解为lock.lock()lock.unlock()中间区域,而lock.lock()则是厕所门的钥匙,lock.unlock()是解完大便出了厕所。这里进入和出入测试都是需要经过lock的。既然lock可以定义为变量,那么就存在重用的可能,那对应上面的剧情,就是lock掌管多个厕所,而且所有这么多测试里面,只能有一个人进去解大便(虽然和现实生活不一样,谁叫他是生活在java的世界里),那就存在严重浪费资源的情况,加入lock掌管10个厕所,那么每次不是都浪费9个厕所?那不是又得憋死线程?所以合理的分配lock管辖的范围是很关键的,最好肯定是一个厕所配备一个lock来管理,但是有些时候确实存在lock需要管理多个厕所的情况。比如:只有两个厕所,由于资源有限,不能一直让人解大便,而不清理厕所(要知道lock可是洁癖!),所以每次只能让一个厕所让人来解大便,而另一个厕所需要进行清理。对应码代码就是存在同一个资源竞争的情况,比如一个资源在两个地方都会修改,防止一个地方的修改被另一个地方的修改覆盖而导致数据的不一致,所以在这两个地方需要共用一个lock对象。

上面简单的阐述了Doug lea通过java实现的锁,其实里面详细的,建议还是阅读一下源码。Doug lea基于这些,实现了很多并发场景需要使用的类,比如读写锁,更加高效的HashMap以及安全的队列等,我这里就不都复制出来了!

上面都是在一个JVM环境中,如果在多个JVM环境中多个服务器中怎么很好的进行并发的控制,从而保持数据的一致性。

##分布式锁## 分布式锁他的大致思想和Doug lea的类似。只是锁的管理不在放在当前的JVM里面,而是放在一个中央服务器上(zookeeper,redis或者数据库等),然后申请和释放锁都需要和中央服务器通信。会在后面的博客中介绍我遇到分布式锁通过zookeeper解决的方案,这里就不做过多的介绍。

此时,听到电脑里的某个软件发出“叮...”,我的32G岛国杂技填满了我的D盘,好好学习一下岛国杂技是怎么练成的!额?手纸用完了.....

标签: java lock 并发
共有 人打赏支持
粉丝 196
博文 36
码字总数 83312
作品 1
×
Bieber
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: