java synchronized简单易懂

        Java多线程编程中,经常涉及到多个线程操作同一个对象的问题,为了保证操作结果符合预期,需要保证操作对象线程安全。当操作对象本身就是线程安全的,则可以直接在多线程环境下调用,例如AtomicInteger,ConcurrentHashMap,StringBuffer等;当操作对象不是线程安全时,则需要在操作时进行加锁,保证执行结果符合预期,例如:HashMap,StringBuilder等。

如何理解synchronized

        当多线程环境下操作一个非线程安全对象时,可以使用synchronized保证操作线程安全。那么synchronized到底是什么,笔者的理解概括为一句话:synchronized是一个对象锁。定义简单,但这里涉及到两个重要概念,一个是对象(请将类和对象进行区分),一个是锁。用现实世界来解释:synchronized就像一把锁,对象就好比门,锁必须加在门上,不然锁就没有任何价值。所以synchronized的本质是找到合适的“门”进行加锁。

一个简单的例子

synchronized i++

        上图是一个简单的i++例子,为了便于理解,笔者将i++分解成3步用add()方法表示,在多线程环境下,会出现两个线程同时执行14行代码的可能,导致+1操作在各自的线程中执行后未能及时复制到主存中,使得15行赋值操作被覆盖。

        add1()和add2()方法采用synchronized关键字进行修饰,保证同一时间只有一个线程操作add()方法,add1()和add2()在synchronized使用上有所不同,add1()synchronized修饰在方法上,add2()修饰在代码块中。上述提到synchronized是一把对象锁,在add2()方法中,可以明确看出来锁加在this对象上。add1()由于修饰的是对象方法,本质上还是加在this对象上,即add1()和add2()方法等价。

一个容易出错的例子

synchronized 错误示范

        该例子将锁加在了i对象上,乍一看好像没有问题,但是可能会出现以下情况:线程1首先获取锁,执行add()代码块,线程2由于线程1正在执行,所以被同步等待,线程3可能由于启动较晚,还没有执行到19行代码,即还没有找“这扇门”,当线程1执行完成后,i所指向的这扇门发生了改变(第15行代码),此时线程2由于线程1执行完成所以成功获得执行权,走到15行代码,而线程三也可以执行add()方法代码块,原因是线程三所进入的这扇门已经不再是方法二进入的那扇门,各自线程进入了不同的“门”,最终导致不一致。

        为了保证"门"地址不被修改,最规范的写法如下:

synchronized final

        创建一扇用final进行修饰的"专用门",防止这扇门的地址被任意的串改,导致锁"失效"。

哪些对象是线程安全,哪些对象不是线程安全?

        这个问题没有一个明确的答案,但是笔者的做法是,在使用一个不熟悉的类时,查看该类的注释和源码,简单了解内部实现来确定该类是否可以在多线程环境下使用,这种方式是最为保险的。

        每个开发人员对于类的设计思路不同,不能简单的通过类名进行判断,举个例子:hbase java客户端有个类为Connection,该类内部有连接池,可以在多线程下使用,只有当程序中需要操作多个hbase集群时,才需要创建多个Connection实例;java jdbc中的Connection表示对DB的单次链接,多线程环境下需要实现额外的线程池,不能直接共用同一个对象。再拿上述代码举例,ThreadCounter可以完全设计成线程安全,即add()方法采用private修饰,并暴露add1()方法,这样在使用ThreadCounter对象时则不需要再添加额外的锁,因为内部已经使用synchronized控制。当ThreadCounter只有add()方法,则表示ThreadCounter不是线程安全的对象,只适用于单线程环境中,在多线程环境下,需要使用者自己加锁控制。

        在设计类时,需要考虑该类的使用场景,不是所有的类需要保证线程安全(加锁的开销较大,绝大部分的类不需要线程安全),但是一些类天生是要在多线程环境下使用的,比如阻塞队列,当队列满或者空时当前线程阻塞,需要被其他线程唤醒,这样的类在设计时就一定需要考虑和保证线程安全问题。

其他注意事项

synchronized static

        当synchronized加在静态方法上,则等价于将锁添加在类对象上(类对象也是一个对象,而并非是类,在java中类对象特殊之处在于类对象是一个单例对象[从相同的classloader中获取],这里先不细讲,等介绍到java反射部分后会涉及这部分内容),所以上述add1()和add2()方法是等价的。

总结

        1. synchronized是对象锁,真正理解synchronized的本质在于理解对象。

        2. 锁的使用难点在于控制最小粒度,保证只有真正需要加锁的地方才加锁,不需要的地方不加锁。这点需要不断的练习和阅读源码才能提高。

        3. synchronized和Lock在使用过程中,在都能满足需求的前提下优先使用synchronized,当需要用到如condition,读写锁,tryLock等特性的情况下再考虑使用Lock。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,194评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,058评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,780评论 0 346
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,388评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,430评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,764评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,907评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,679评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,122评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,459评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,605评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,270评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,867评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,734评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,961评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,297评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,472评论 2 348

推荐阅读更多精彩内容