Java中的synchronized关键字用于保证同步访问,避免出现多线程并发访问共享资源的问题,保证程序的正确性和一致性。在JVM中,synchronized的实现原理是通过Java对象头中的一个有关锁的标识位来实现的,具体的底层实现原理如下:
Java对象头
Java对象在堆中的数据结构是由对象头和实例数据两部分组成的,其中对象头占用了8个或者12个字节(64位JVM为12字节,32位JVM为8字节)。JVM对象头中主要包含了两部分信息:对象的hashCode以及对象的锁信息,锁信息主要有以下内容:
- Mark Word:存储对象的标志位信息,主要有锁状态及一些标志位;
- Klass Pointer:指向该对象对应的类元信息指针。
synchronized的实现原理
synchronized 关键字可以用来修饰普通方法、静态方法和代码块,这里主要讲解普通方法的synchronized实现原理。
普通方法的synchronized底层实现主要包含四个步骤:
- 当线程A来执行synchronized修饰的方法时,首先会检查该方法的锁状态是否处于 ''Unlocked" 状态;
- 如果处于 ''Unlocked" 状态,线程A则会获得该方法所对应对象的锁并将锁的状态设置为 ''Locked";
- 如果处于 ''Locked" 状态,线程A就会进入同步队列(等待队列);因为该方法是被synchronized修饰的,只能被一个线程执行,因此如果有多个线程来调用该方法,则会进入同步队列中阻塞等待执行;
- 当同步队列中线程的锁状态被释放时,唤醒队列中的线程,然后其中一条线程再次尝试获取该方法所对应对象的锁并将锁的状态设置为 ''Locked",取得锁的线程进入执行状态,其他线程仍然进入同步队列中等待。
锁优化
由于synchronized在实现同步的时候会带来一定的系统开销和调度开销,因此,JVM提供了一些锁优化来提高程序的执行效率,其中包括:
自旋锁
自旋锁是通过循环CAS(Compare-And-Swap)操作实现的,当线程发现自己要获取的锁已经被其他线程获取时,它不会马上进入阻塞状态,而是采用循环的方式不断尝试获取锁(重试),直到获取锁成功或者达到一定重试次数为止。自旋锁适用于锁竞争情况不是很激烈的场景。
锁消除
在程序执行过程中,如果JVM发现某些锁不会被多线程访问,那么JVM就会自动把锁消除掉,从而避免不必要的锁竞争。
例如:
public String getString(String str1, String str2) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(str1).append(str2);
return stringBuffer.toString();
}
上述代码中,由于StringBuffer对象只会在当前方法中使用,并不会被其他的线程访问,因此JVM可以自动把该方法中的锁消除掉,从而提高方法的执行效率。
轻量级锁
轻量级锁是为了避免线程在执行同步代码块时频繁地进入阻塞状态而引入的一种优化方式。JVM使用CAS操作,把当前线程的锁对象的MarkWord复制到线程的本地变量中,然后再尝试用CAS操作将线程本地变量的MarkWord替换回原对象的MarkWord。
偏向锁
偏向锁是JVM提供的一种轻量级锁优化策略,它适用于哪些只会有一个线程访问的情况。偏向锁的核心思想是:如果一个线程在访问一个锁对象之前,该锁对象没有被其他线程访问过,那么该线程就可以获得该锁,并把MarkWord中的偏向锁标识符设置为该线程的唯一标识符。这样,如果以后再有该线程访问该锁对象,那么该线程就可以直接获取锁,而不再需要竞争。如果其他线程要访问该锁对象,则会先调用CAS操作来检测当前锁标识符是否等于线程的唯一标识符,如果是,则获取锁成功,否则锁标识符被升级为轻量级锁。
例如:
public class Test {
public static void main(String[] args) {
synchronized (Test.class) {
synchronized (Test.class) {
System.out.println("Nested Lock!");
}
}
}
}
在上述代码中,当线程A第一次执行synchronized块时,会尝试获取Test.class的锁并将该锁的状态设置为 ''Locked",然后线程A对应的锁标识符会被设置为A的特定标识符。当线程A第二次执行synchronized块时,会检测Test.class的锁标识符是否等于线程A的特定标识符,如果等于,则获取锁成功,否则尝试CAS操作将该锁的状态从 ''Unlocked" 修改为 ''Locked";如果修改成功,则锁标识符被设置为A的特定标识符,获取锁成功,否则线程A会将该锁升级为轻量级锁。
综述: 以上就是Java synchronized的底层实现原理以及锁优化的攻略,通过深入了解synchronized关键字的底层实现原理以及锁的优化机制,可以使我们在程序开发过程中更加高效地使用多线程技术,提高程序的性能。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java synchronized底层实现原理以及锁优化 - Python技术站