1:结论
对于资源共享的问题,我们常常会使用到一个关键字(synchronized) ,该关键字可以使用到成员方法,代码块,静态方法上,使用到不同的地方,以为着采用的锁对象不一样。(在这里个人理解认为将每个对象作为锁去认识难免有点迷糊,因此我认为将每个锁对象直接解释为线程的监管者更好。)对于采用了synchronized关键字的临界区,根据使用线程的时间分为(偏向锁,无锁),以及是否有线程竞争根据竞争时间上是否激烈分为轻量级锁和重量级锁,因此synchronized本事并不一定就是重量级锁。
2:对象头
对象头里面分别有:
- Mark Word(标记字): Mark Word 是对象头中最重要的部分,它包含了对象的哈希码、锁状态(用于锁定对象的状态,例如偏向锁、轻量级锁、重量级锁等)、垃圾回收信息等,根据不同锁的状态进行切换(偏向锁,无锁、轻量级锁和重量级锁)
- 指向类的指针(Class Pointer): 对象头中通常包含一个指向对象所属类的指针,用于确定对象的类信息。这个指针可以帮助虚拟机定位对象的方法表、字段布局等类相关的信息。
- 对象内部成员变量信息。
3:代码
import lombok.extern.slf4j.Slf4j;
import org.openjdk.jol.info.ClassLayout;
@Slf4j(topic = "JUC.test001")
public class test001 {
public static void main(String[] args){
String arch = System.getProperty("sun.arch.data.model");
System.out.println("JVM位数:" + arch + "-Bit");
test002();
}
/**
* 默认的对象头(64位jvm)
* 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
* 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
* e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
*/
public static void test001(){
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
/**
* 偏向锁
* 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
* 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
* e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
*/
public static void test002(){
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Object o = new Object();
log.debug(ClassLayout.parseInstance(o).toPrintable());
synchronized (o) {
log.debug(ClassLayout.parseInstance(o).toPrintable());
try {
Thread.sleep(1000); // 模拟持有锁的操作
log.debug("exiting----------------------------------------------------------------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (o) {
log.debug(ClassLayout.parseInstance(o).toPrintable());
try {
Thread.sleep(1000); // 模拟持有锁的操作
log.debug("exiting----------------------------------------------------------------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (o) {
log.debug(ClassLayout.parseInstance(o).toPrintable());
try {
Thread.sleep(1000); // 模拟持有锁的操作
log.debug("exiting----------------------------------------------------------------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (o) {
log.debug(ClassLayout.parseInstance(o).toPrintable());
try {
Thread.sleep(1); // 模拟持有锁的操作
log.debug("exiting----------------------------------------------------------------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Thread T1 =new Thread(new Runnable() {
@Override
public void run() {
synchronized (o) {
log.debug("升级为轻量级锁");
log.debug(ClassLayout.parseInstance(o).toPrintable());
}
}
},"Thread_001");
T1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (o){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.debug("升级为重量级锁");
log.debug(ClassLayout.parseInstance(o).toPrintable());
}
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.debug("ALL Tread have finished");
log.debug(ClassLayout.parseInstance(o).toPrintable());
log.debug("再一次变为偏向锁---------------------------------------------------------");
for (int i = 0; i < 1000; i++) {
synchronized (o) {
if(i==500 || i==999){
log.debug(ClassLayout.parseInstance(o).toPrintable());
}
try {
Thread.sleep(100); // 模拟持有锁的操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
改代码的目的测试了:对象锁状态从偏向锁变为轻量级锁再变为重量级锁的过程,同时得出一旦多个线程竞争过锁,就不会再变成偏向锁。所以:
-
偏向锁: 偏向锁是一种特殊的轻量级锁,它针对只有一个线程访问锁的情况进行了优化。在偏向锁情况下,锁会偏向于第一个访问它的线程,这个线程获得锁后,不需要再进行同步操作,从而减少了锁的争用和性能开销。偏向锁在多线程环境下会自动升级为轻量级锁或重量级锁,具体取决于其他线程的竞争情况。
-
轻量级锁: 在多个线程竞争同一个锁对象时,会使用轻量级锁来减少锁的争用。轻量级锁使用了自旋等待来避免线程的阻塞,从而减少了线程上下文切换的开销。如果自旋等待不成功,锁会升级为重量级锁。(不断的由一个线程得到该锁还是轻量级锁,重入锁)
-
重量级锁通过变会无锁又可以变为轻量级锁。
4: 重量级锁的实现原理
在Mark Word 中,当为重量级锁的时候会指向一个jvm层面维护的Monitor对象,该对象包括3个重要的数据结构分别是EntryList(维护着竞争关系的线程),Owner 将资源分配给那个线程(指向),WaitSet(维护着具有通讯关系的线程)