【Java虚拟机】 Java锁解析:深入理解各类锁的工作原理

锁的分类

为了解决多线程并发环境下的线程安全问题,Java提出了锁的机制。与我们之前学习MySQL解决并发下事务的问题而提出不同的锁一样。
锁的机制解决了线程安全问题,为了降低锁对性能问题的影响,针对不同的场景,Java对锁添加了不同的特性,满足不同的需求,将锁进行优化。
针对不同的特性与场景对锁进行了不同的分类:

阻塞与非阻塞

阻塞与非阻塞的区别在于当前线程在获取锁失败时是否让出CPU时间,挂起线程
在阻塞情况下,当前线程需要在获取失败时让出CPU时间,当占用锁的线程执行完任务后,释放锁时,再唤醒当前线程重新获取锁。
但是挂起和唤醒线程,都需要CPU在用户态和内核态之间切换,这些操作都会消耗系统资源,在同步块逻辑处理简单的情况下,切换线程带来的系统消耗会大于节约的,得不偿失。
因此Java引入了自旋锁,来实现非阻塞的同步
所谓自旋就是在获取锁失败时,不放弃CPU时间当前线程进行空循环(自旋),自旋结束如果等待的锁被释放就可以直接获取锁,从而避免线程切换


自旋锁的实现原理是CAS( Compare And Swap),在Java的原子类AtomicInteger中就使用了Unsafe的CAS操作:

do-whlie循环中的,如果set数值失败就会一直循环执行,直到成功。
自旋锁避免了线程切换开销,但是如果长时间占用CPU则会浪费CPU资源,因此可以通过-XX:PreBlockSpin来控制自旋的次数。
在JDK 1.6还引入了自适应自旋锁,自旋时间可以根据上次在同一个锁上的自旋时间锁的拥有者的状态来决定。

乐观与悲观

乐观与悲观是其实指的是一种思想。与MySQL事务详解(三):脏写与幻读的绝路—锁一文中提到的乐观与悲观一样。
乐观假定当前线程修改数据时,没有其他线程与其发生竞争,不主动加锁,更新数据时如果被其他线程更新,
与预期不一致,则可以给出不同的补偿操作保持数据的一致。上述提高的CAS算法就是乐观锁的一种实现
悲观则与乐观相反,操作时会主动加锁,保持自己对资源的独享,避免其他线程的修改。Synchronized、Lock都是悲观锁

共享与排他

共享与排他同样是一种思想,顾名思义,两者的区别在于当前的锁是否可以同时被多个线程所持有
Synchronized、Lock只能被一个线程所持有,锁住的资源只能被当前持有锁的线程所访问,所以是排他锁。
但是排他锁带来一个问题,针对读多写少的业务场景,每次读去数据都要独占资源,这会降低读的性能
为此引入了共享锁,可以被多个线程锁持有,那么共享的资源就可以同时被多个线程所访问,注意的是共享锁只能用于读数据,且加了共享锁就不能再加排他锁
Java中的ReentrantReadWriteLock里面的ReadLock就是共享锁,通过读写分离,ReentrantReadWriteLock提高的读操作的并发性,从而提高的性能。

可重入与不可重入

可重入锁,又称为递归锁,当一个线程在外层方法中获取锁之后,内层方法中再次获取同一个锁时,会自动获取而不会因为之前的锁没有释放而阻塞。

public class TestSynchronized {
    public synchronized void levelOne(){
        System.out.println("level one");
        levelTwo();
    }
    public synchronized void levelTwo(){ //同一个线程会直接自动获取到锁
        System.out.println("level two");
    }
}

可重入锁可以解决上述场景下出现的死锁问题
可重入锁在被上锁时会判断当前线程是否与持有锁的线程为同一个线程,如果是则直接获取成功,计数器自增,释放锁时计数器自减。
Synchronized、ReentrantLock都是可重入锁,NonReentrantLock为非可重入锁。

公平与非公平

公平锁:多个线程获取锁的顺序按照其申请锁的顺序依次获取,先到先得。公平锁不会出现饿死的现象,但整体吞吐量相对非公平锁要低。
非公平锁:线程获取锁时直接去尝试获取锁,加锁失败进入队列等待。非公平会出现饿死线程,但整体吞吐量相对公平锁要高。

在上述ReentrantLock的代码示例中,会先判断是否有其他线程在排队等待hasQueuedPredecessors(),表明这是一个公平锁的代码。 ReentrantLock也有非公平锁的实现,如下:

可以看到,公平与非公平的唯一区别就在于是否会判断等待队列

偏向、轻量级与重量级

在JDK 1.6前,Synchronized关键字的加锁机制是依赖于底层的操作系统的Mutex Lock来实现线程同步。这种加锁机制的锁被称为“重量级锁”。
在重量级锁下,会出现大量线程的阻塞与唤醒操作导致CPU状态的频繁切换而消耗大量系统资源,从而带来性能问题
因此,在JDK 1.6 引入了偏向锁、轻量级锁来对Synchronized进行优化。

轻量级锁:在我们的大部分开发应用中,对共享对象进行长时间的独享占用属于少数情况。在大多数情况,共享对象处于非锁定或者短时间的锁定状态。
基于这种情况下,如果一直使用重量级锁对共享数据进行独享锁定,必然会导致性能上的下降。
轻量级锁使用CAS操作将锁定对象的Mark Word更新为指向当前线程栈帧中的Lock Record指针,如果操作成功,则加锁成功,否则,则进入自旋状态等待。
CAS操作避免了CPU状态的切换,从而节约了系统资源

:当一个线程在占用锁,另一个线程在自旋等待时,又有一个线程来竞争同一个锁时(即超过2个线程竞争锁时),轻量级锁不在有效,会膨胀为重量级锁

轻量级锁解决了在没有多线程竞争的情况,因CPU状态切换导致的性能消耗
偏向锁:轻量级锁提高了效率,但是每次加锁依然会有CAS操作。偏向锁就是为了解决这个问题:在没有竞争的情况下,消除CAS同步原语。 偏向锁,顾名思义,偏向于第一个加锁成功的线程。
在线程第一次通过CAS加锁成功后,如果锁没有被其他线程竞争,持有该锁的线程就不需要同步,自动获取锁
注意:当有其他线程尝试竞争偏向锁时,偏向锁失效,膨胀为轻量级锁

锁消除与锁粗化

锁消除:虚拟机在编译期间,对代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。
锁消除依赖的是编译期间的逃逸分析【Java虚拟机】内存分配与回收策略),堆上数据不会发生逃逸被其他线程所访问,就可以认为是线程私有的,同步锁自然可以消除。

锁粗化: 在编程中,我们推荐将锁的粒度尽量缩小,减少需要同步的数据量,降低锁等待。
但是假如一系列的连续操作都是对同一个对象反复加解锁甚至在循环体内进行加解锁,就会频繁进行同步操作而消耗性能。
在这种情况下,虚拟机会将加锁的范围扩大(即粗化),降低同步频率以提高性能。

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

推荐阅读更多精彩内容