synchronized

文章目录

  • 一、synchronized的特性
  • 二、synchronized的使用
  • 三、synchronized的锁机制
    • 3.1锁升级
    • 3.2锁消除
    • 3.3锁粗化

一、synchronized的特性

①对于“悲观乐观锁”,是自适应的。
②对于“重量轻量锁”,是自适应的。
③对于“自旋挂起等待锁”,是自适应的。
④不是读写锁。
⑤是可重入锁。
⑥是非公平锁。
初始情况下,synchronized如果预测到当前锁冲突的概率不大,就会以乐观锁的模式来运行(即轻量级锁,基于自旋锁的方式来实现),实际使用过程中,如果发现锁冲突的情况比较多,synchronized会升级为悲观锁(即重量级锁,基于挂起等待锁的方式来实现)。

二、synchronized的使用

synchronized要依赖锁对象同时搭配代码块使用。

 Object lock = new Object();
        synchronized (lock) {
            //working
        }

三、synchronized的锁机制

3.1锁升级

锁升级:无锁→偏向锁→自旋锁→重量级锁。
锁升级的过程是单向的,是JVM在性能和线程安全之间尽量做出的权衡。
偏向锁不是真的 “加锁”, 只是给锁的对象头中做一个 “偏向锁的标记”, 记录这个锁属于哪个线程。
如果后续没有其他线程来竞争该锁, 那么就不用进行其它操作了(避免了加锁解锁的开销)。
如果后续有其他线程来竞争该锁(刚才已经在锁对象中记录了当前锁属于哪个线程了, 很容易识别当前申请锁的线程是不是之前记录的线程), 那就取消原来的偏向锁状态, 进入轻量级锁状态。
这样就可以既能保证效率又能保证线程安全,偏向锁是“懒汉模式”的一种体现。

3.2锁消除

锁消除是一种编译器优化的手段,编译器会自动针对加锁的代码做出判断,如果编译器觉得某个场景下不需要加锁,此时就会把synchronized给优化掉,编译器只会在非常有把握的情况下才会进行锁消除(触发锁消除的概率不是很高)。
比如StringBuilder不带synchronized,StringBuffer带synchronized,如果是在单线程环境下使用StringBuffer,此时编译器会自动把synchronized优化掉。
锁消除是在编译阶段触发的,这时还没到运行阶段。偏向锁是发生在运行阶段的事情,如果在编译阶段无法判断这个锁是否需要消除,则这个锁只能保留到运行阶段。

3.3锁粗化

锁粗化也是一种编译器优化的手段。
synchronized代码块里面的代码越多锁的粒度就越粗,代码越少锁的粒度就越细。
粒度细的锁能够并发执行的逻辑更多,更有利于利用多核cpu资源。但是粒度细的锁被反复进行加锁解锁(涉及反复的锁竞争),可能实际效果还不如粒度粗的锁。
以下为粒度细的锁:

for(){
    synchronized(lock){
        n++;
    }
}

以下为粒度粗的锁:

synchronized(lock){
    for(){
        n++;
    }
}

比如以上锁的粗化避免了一些不必要的锁竞争,提高了程序的执行效率。

你可能感兴趣的:(java,开发语言,多线程)