synchronized是java并发中解决同步问题的一个重量级方案,为什么是重量级方案呢,因为使用synchronized会发生对内核态和用户态切换的操作,简单来说就是会发生应用和操作系统之间的交互,尤其再并发程度比较高的时候,效率十分低下。
原理
那synchronized是如何解决同步的呢?先不考虑锁优化的流程,就单纯讨论synchronized,整体逻辑其实跟volatile差不多,都是保证使用的共享变量都是直接从主内存获取而不再工作内存中获取,当退出了synchronized的时候,将修改的值重新写会到主内存中。 synchronized主要分为两块,锁对象和同步代码块,通过这两个组合保证同步和原子性。
锁对象,又可以分为类和实例对象。类作为锁对象的范围会更大。因为类是整个jvm只会有一个,实例对象可以实例化多个。而如何判断是使用了类对象还是实例对象
1.修饰静态方法
public static synchronized void test(){
i++;
}
2.锁类的字节码
synchronized(SynchronizedTest.class){
test();
}
这两种都是会将类对象作为锁对象。
1.修饰普通方法
public synchronized void test(){
i++;
}
2.实例化对象
synchronized(synchronizedTest){
test();
}
这两种都是会将实例对象作为锁对象。
同步代码块,为什么说synchronized是重量级锁,jvm系统会在编译成字节码的时候,在同步代码块开始和结尾添加monitorenter和monitorexit字节码指令,调用操作系统互斥量系统保证同步。但是如果是方法的话会在该方法上标志ACC_SYNCHRONIZED。逻辑也是跟monitorenter和monitorexit一致。
总结
synchronized是通过调用操作系统的互斥变量保证线程的同步,所以会存在线程上下文切换的开销。主要分为两块锁对象和同步代码块。当我们使用了synchronized,jvm在编译成字节码的时候,会在代码块的开始和结束阶段,添加monitorenter和monitorexit字节码指令(如果是方法则添加ACC_SYNCHRONIZED指令)。当第一个线程执行到monitorenter会在进行计数,直到执行到monitorexit进行数量-1。否则当其他线程执行到monitorenter获取对象的锁时,就会挂起,直到当前执行线程将锁释放掉。