转载

详解java多线程之synchronized

​ 同步代码块/同步方法是通过synchronized关键字提供的java语言层面的线程同步工具,在编译成字节码文件后,生成对应的jvm字节码指令或标记。上一篇博客中介绍了对象锁相关知识,其中提到锁最终可能会升级为重量级锁,即对象头mark word中锁标记为10,此时,mark word中其他位指向了一个互斥量,它是一个锁对象的监视器对象,这篇文章主要介绍synchronized和监视器对象。

3.3.1 synchronized

synchronized 是一个jdk提供的可重入的,非公平的内置锁,可以分为 同步块普通同步方法静态同步方法

  • 无同步

先从一个没有同步的普通对象开始

public class SynchronizedTest {
    
    private static int count;

    public int getAndIncrement() {
        return count++;
    }
}
复制代码

观察字节指令:

详解java多线程之synchronized

可以看到getAndIncrement方法就是1.加载count的值,2.与1相加,3.写回。

  • 同步代码块

    public class SynchronizedTest {
        
        private static int count;
    
        private Object lock = new Object();
    
        public int getAndIncrement() {
            synchronized (lock) {
                return count++;
            }
        }
    }
    
    复制代码

    使用代码同步块,锁对象为lock,字节码指令如下:

    详解java多线程之synchronized

    可以看到加了synchronized同步块之后,字节码里插入了1个monitorenter,2个monitorexit指令,而且原来的代码异常会被捕获,这样在异常时都能转到异常处理指令(19行)处处理,最终退出监视器对象。

  • 同步方法(非静态)

    public class SynchronizedTest {
        
        private static int count;
    
        public synchronized int getAndIncrement() {
           return count++;
        }
    }
    复制代码

    字节码指令:

    详解java多线程之synchronized

    可以看到同步方法,并没有在字节码中插入monitorenter,monitorexit指令,它有一个 ACC_SYNCHRONIZED 标志,在执行方法调用时,如果有此标志,则首先要获取monitor,跟同步代码块同理,此时的锁对象为方法所属对象(this)。

  • 同步方法(静态)

    public class SynchronizedTest {
        
        private static int count;
    
        public static synchronized int getAndIncrement() {
           return count++;
        }
    }
    复制代码

    字节码:

    详解java多线程之synchronized

    静态同步方法跟普通同步方法基本相同,不同点在于它的锁对象是 Class<SynchronizedTest> 对象。

3.3.2 监视器(monitor)

​ 监视器(或管程)是对多线程并发访问共享内存进行管理的一种方式,它封装了多线程的两个核心概念:互斥与同步(协作/通信),一个monitor对象封装了enter/exit/condition variable/wait/signal等方法和变量。

详解java多线程之synchronized

​ 上图是MESA管程模型,可以看到线程在访问之前都要先在Entry队列排队

java中monitor与上面模型相似,但实现略有不同,每一个java对象都有一个monitor与之关联,它是实现线程同步的关键。

详解java多线程之synchronized

​ java monitor 机制

可以看到,java monitor同样有入口队列,等待队列,但等待队列的线程可以直接去获取锁而不需要先进入入口队列,所以synchronized是一个非公平锁实现。

再来看一下hotspot的monitor实现 objectMonitor

详解java多线程之synchronized
  • _EntryList:入口队列
  • WaitSet:调用wait()时的等待队列
  • _owner:当前持有监视器的线程
  • _count:获取监视器次数(暗示可重入特性),相当于信号量,对此进行(P,V)操作

任意时刻,只有一个线程处于活动状态(持有监视器),所以获取到监视器就是获取到了锁,此时,锁对象对象头mark word锁标志变成10,并指向了该监视器对象,释放了监视器就释放了锁。线程的同步(wait/notify/notifyAll)通过monitor来实现,这也是为什么wait/notify/notifyAll要放在synchronized作用范围内。

关于中断,synchronized中,等待线程是无法响应中断的(Thread.interupt())。

更多文章,见公-众-号

详解java多线程之synchronized
原文  https://juejin.im/post/5eb3b547f265da7bc16964cf
正文到此结束
Loading...