Java 多线程同步 锁机制与synchronized深入解析

 更新时间:2013年09月11日 09:04:53   作者:  
从尺寸上讲,同步代码块比同步方法小。你可以把同步代码块看成是没上锁房间里的一块用带锁的屏风隔开的空间

打个比方:一个object就像一个大房子,大门永远打开。房子里有很多房间(也就是方法)。这些房间有上锁的(synchronized方法), 和不上锁之分(普通方法)。房门口放着一把钥匙(key),这把钥匙可以打开所有上锁的房间。另外我把所有想调用该对象方法的线程比喻成想进入这房子某个 房间的人。所有的东西就这么多了,下面我们看看这些东西之间如何作用的。

在此我们先来明确一下我们的前提条件。该对象至少有一个synchronized方法,否则这个key还有啥意义。当然也就不会有我们的这个主题了。

一个人想进入某间上了锁的房间,他来到房子门口,看见钥匙在那儿(说明暂时还没有其他人要使用上锁的房间)。于是他走上去拿到了钥匙,并且按照自己 的计划使用那些房间。注意一点,他每次使用完一次上锁的房间后会马上把钥匙还回去。即使他要连续使用两间上锁的房间,中间他也要把钥匙还回去,再取回来。

因此,普通情况下钥匙的使用原则是:“随用随借,用完即还。”

这时其他人可以不受限制的使用那些不上锁的房间,一个人用一间可以,两个人用一间也可以,没限制。但是如果当某个人想要进入上锁的房间,他就要跑到大门口去看看了。有钥匙当然拿了就走,没有的话,就只能等了。

要是很多人在等这把钥匙,等钥匙还回来以后,谁会优先得到钥匙?Not guaranteed。象前面例子里那个想连续使用两个上锁房间的家伙,他中间还钥匙的时候如果还有其他人在等钥匙,那么没有任何保证这家伙能再次拿到。 (JAVA规范在很多地方都明确说明不保证,象Thread.sleep()休息后多久会返回运行,相同优先权的线程那个首先被执行,当要访问对象的锁被 释放后处于等待池的多个线程哪个会优先得到,等等。我想最终的决定权是在JVM,之所以不保证,就是因为JVM在做出上述决定的时候,绝不是简简单单根据 一个条件来做出判断,而是根据很多条。而由于判断条件太多,如果说出来可能会影响JAVA的推广,也可能是因为知识产权保护的原因吧。SUN给了个不保证 就混过去了。无可厚非。但我相信这些不确定,并非完全不确定。因为计算机这东西本身就是按指令运行的。即使看起来很随机的现象,其实都是有规律可寻。学过 计算机的都知道,计算机里随机数的学名是伪随机数,是人运用一定的方法写出来的,看上去随机罢了。另外,或许是因为要想弄的确定太费事,也没多大意义,所 以不确定就不确定了吧。)

再来看看同步代码块。和同步方法有小小的不同。

1.从尺寸上讲,同步代码块比同步方法小。你可以把同步代码块看成是没上锁房间里的一块用带锁的屏风隔开的空间。

2.同步代码块还可以人为的指定获得某个其它对象的key。就像是指定用哪一把钥匙才能开这个屏风的锁,你可以用本房的钥匙;你也可以指定用另一个房子的钥匙才能开,这样的话,你要跑到另一栋房子那儿把那个钥匙拿来,并用那个房子的钥匙来打开这个房子的带锁的屏风。

记住你获得的那另一栋房子的钥匙,并不影响其他人进入那栋房子没有锁的房间。

为什么要使用同步代码块呢?我想应该是这样的:首先对程序来讲同步的部分很影响运行效率,而一个方法通常是先创建一些局部变量,再对这些变量做一些 操作,如运算,显示等等;而同步所覆盖的代码越多,对效率的影响就越严重。因此我们通常尽量缩小其影响范围。如何做?同步代码块。我们只把一个方法中该同 步的地方同步,比如运算。

另外,同步代码块可以指定钥匙这一特点有个额外的好处,是可以在一定时期内霸占某个对象的key。还记得前面说过普通情况下钥匙的使用原则吗。现在不是普通情况了。你所取得的那把钥匙不是永远不还,而是在退出同步代码块时才还。

还用前面那个想连续用两个上锁房间的家伙打比方。怎样才能在用完一间以后,继续使用另一间呢。用同步代码块吧。先创建另外一个线程,做一个同步代码 块,把那个代码块的锁指向这个房子的钥匙。然后启动那个线程。只要你能在进入那个代码块时抓到这房子的钥匙,你就可以一直保留到退出那个代码块。也就是说 你甚至可以对本房内所有上锁的房间遍历,甚至再sleep(10*60*1000),而房门口却还有1000个线程在等这把钥匙呢。很过瘾吧。

在此对sleep()方法和钥匙的关联性讲一下。一个线程在拿到key后,且没有完成同步的内容时,如果被强制sleep()了,那key还一直在 它那儿。直到它再次运行,做完所有同步内容,才会归还key。记住,那家伙只是干活干累了,去休息一下,他并没干完他要干的事。为了避免别人进入那个房间 把里面搞的一团糟,即使在睡觉的时候他也要把那唯一的钥匙戴在身上。

最后,也许有人会问,为什么要一把钥匙通开,而不是一个钥匙一个门呢?我想这纯粹是因为复杂性问题。一个钥匙一个门当然更安全,但是会牵扯好多问题。钥匙的产生,保管,获得,归还等等。其复杂性有可能随同步方法的增加呈几何级数增加,严重影响效率。这也算是一个权衡的问题吧。为了增加一点点安全性,导致效率大大降低,是多么不可取啊。

相关文章

  • Java中四种9*9乘法表的实现方式(附代码)

    Java中四种9*9乘法表的实现方式(附代码)

    这篇文章主要介绍了Java中四种9*9乘法表的实现方式(附代码),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • Java Stream API与函数式编程的实战详解

    Java Stream API与函数式编程的实战详解

    Java 8引入的Stream API和函数式编程特性,彻底改变了Java开发者编写代码的方式,本文将深入探讨Java Stream API与函数式编程的核心概念,最佳实践以及性能优化技巧,感兴趣的小伙伴可以了解下
    2025-06-06
  • Springboot之@Controller注解不生效问题及解决

    Springboot之@Controller注解不生效问题及解决

    这篇文章主要介绍了Springboot之@Controller注解不生效问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10
  • idea离线使用jrebel的超详细教程

    idea离线使用jrebel的超详细教程

    IDEA上原生是不支持热部署的,一般更新了 Java 文件后要手动重启 Tomcat 服务器,才能生效,下面通过本文给大家分享idea离线使用jrebel的超详细教程(亲测有效),感兴趣的朋友一起看看吧
    2020-12-12
  • 使用JPA插入枚举类型字段

    使用JPA插入枚举类型字段

    这篇文章主要介绍了使用JPA插入枚举类型字段,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Java Files和Paths的使用demo详解

    Java Files和Paths的使用demo详解

    Java Files和Paths是Java 7中引入的新API,用于处理文件和目录,Files类提供了许多有用的静态方法来操作文件和目录,而Path类则表示文件系统中的路径,这篇文章主要介绍了Java Files和Paths的使用详解,需要的朋友可以参考下
    2023-03-03
  • Java使用Spire.PDF for Java实现轻松替换PDF文本

    Java使用Spire.PDF for Java实现轻松替换PDF文本

    在数字化办公日益普及的今天,PDF 文档因其跨平台、内容固定的特性,成为企业合同、报告、发票等各类文档的首选格式,本文将聚焦于如何利用 Java结合Spire.PDF for Java实现 PDF 文档中文本的自动化替换
    2025-11-11
  • Java中关于Collections集合工具类的详细介绍

    Java中关于Collections集合工具类的详细介绍

    Java提供了一个操作Set、List和Map等集合的工具类:Collections,该工具提供了大量方法对集合元素进行排序、查询和修改等操作,还提供了将集合对象设置为不可变、对集合对象实现同步控制等方法
    2021-09-09
  • 容器环境的JVM内存设置实践记录

    容器环境的JVM内存设置实践记录

    Docker和K8S的兴起,很多服务已经运行在容器环境,对于java程序,JVM设置是一个重要的环节,这里总结下我们项目里的最佳实践,对容器环境的JVM内存相关知识感兴趣的朋友一起看看吧
    2022-03-03
  • Java中文件创建于写入内容的常见方法

    Java中文件创建于写入内容的常见方法

    在日常开发中,肯定离不开要和文件打交道,今天就简单罗列一下平时比较常用的创建文件并向文件中写入数据的几种方式,希望对大家有一定的帮助
    2023-10-10

最新评论