二、深入剖析线程安全性问题与底层原理

1.什么是线程安全?线程安全会带来哪些底层问题?

2.分析保证线程安全的三个性质-原子性、可见性、有序性

3.多场景剖析未保证原子性带来的问题

package imooc.atomic;public class AtomicTest {public static void main(String[] args) throws InterruptedException {BankAccount bankAccount = new BankAccount(1000);
//        Object lock = new Object();//存钱的线程Thread thread = new Thread(() -> {for (int i = 0; i < 3000; i++) {
//                synchronized (lock){bankAccount.deposit(100);
//                }}});//消费的线程Thread thread1 = new Thread(() -> {for (int i = 0; i < 3000; i++) {
//                synchronized (lock){bankAccount.withdraw(100);
//                }}});thread.start();thread1.start();thread.join();thread1.join();System.out.println(bankAccount.getBalance());}
}class BankAccount{private int balance;public BankAccount(int balance) {this.balance = balance;}/*** 存钱* @param amount*/public void deposit(int amount){System.out.println();balance+=amount;}/*** 消费* @param amount*/public void withdraw(int amount){balance-=amount;}public int getBalance() {return balance;}
}

期望:

        最终还是1000 

运行结果:

       

每次运行结果不一样。

并发执行:

4.如何保证原子性操作?

注意:Atomic类不能完全替代synchronized。

5.synchronized关键字可在哪些地方将代码“锁”住?

解释:

        synchronized后面的小括号里,可以放任意对象,叫做锁对象。

代码示例:

package imooc.atomic.lock;public class Executor {Object lock = new Object();public void method1(){synchronized (lock){String threadName = Thread.currentThread().getName();for (int i = 0; i < 5; i++) {System.out.println("Thread "+threadName+" 正在执行");
//                try {
//                    Thread.sleep(1000);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }}}}
//    public synchronized static void method2(){
//        String threadName = Thread.currentThread().getName();
//        for (int i = 0; i < 5; i++) {
//            System.out.println("Thread "+threadName+" 正在执行");
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//        }
//    }
}
package imooc.atomic.lock;public class ThreadTestSync extends Thread{Executor executor;
//    boolean flag = false;public ThreadTestSync(Executor executor,boolean flag) {this.executor = executor;
//        this.flag = flag;}@Overridepublic void run() {
//        if(flag){
//            executor.method1();
//        }else{
//            executor.method2();
//        }executor.method1();}
}
package imooc.atomic.lock;public class TestCase {public static void main(String[] args) {Executor executor = new Executor();ThreadTestSync testSync = new ThreadTestSync(executor,true);ThreadTestSync testSync1 = new ThreadTestSync(executor,false);testSync.start();testSync1.start();}
}

运行结果:

锁this,也就是当前实例:

运行结果:

不同实例:

运行结果:

锁class对象:所有实例公用一把锁

运行结果:

比如,只需要锁住第一、二、三步: 

比如,第三步不需要锁:

如果第三步非常非常耗时间,我们把他提出来,如果第三步只是普通计算,还不如直接放到锁里,因为加锁、解锁非常的耗费性能。

 同一实例,锁加在方法上:

运行结果:

不同实例:

 同步代码块锁对象是this时,和同步方法使用的是同一把锁:

运行结果:

代码块的锁对象为class时,和修饰静态方法是同一把锁:

运行结果:

6.使用wait和notify方法实现生产者消费者模式

在没有拿到锁的情况下,调用wait()会怎么样?

package imooc.waitnotify;public class TestWaitAndNotify {public static void main(String[] args) throws InterruptedException {Object o = new Object();
//        o.wait();o.notify();}}

运行工结果:

所以,我们不能在没拿到锁的情况下,直接调用wait()或notify()。

更好的方法:

具体实现:

运行结果:

7.使用synchronized关键字实现的锁内存存储原理深入剖析

为什么需要Monitor?

java早期为了实现方便,直接使用操作系统提供的能力(互斥锁,也叫重量级锁),如果需要使用重量级锁,就需要从用户态转为内核态。

Monitor实现重量级锁。

java对象是怎么关联Monitor?

对齐填充 - java对象的数据大小,如果刚好能被8整除,就需要对齐填充数据,否则需要补充对齐填充数据,能够被8整除。

代码演示:

package imooc.atomic.lock;import org.openjdk.jol.info.ClassLayout;/*** @Author: Alfred* @ModuleOwner: Alfred* @Description:*/
public class TestSync {public static void main(String[] args) {Object lock = new Object();/*** 使用ClassLayout,* 需要引入*      <dependency>* 			<groupId>org.openjdk.jol</groupId>* 			<artifactId>jol-core</artifactId>* 			<version>0.9</version>* 		</dependency>*//*** System.out.println(ClassLayout.parseInstance(lock).toPrintable());* 运行结果:* "C:\Program Files\Java\jdk-17.0.5\bin\java.exe" "-javaagent:C:\Program_Files\JetBrains\IntelliJ IDEA 2023.2.5\lib\idea_rt.jar=53687:C:\Program_Files\JetBrains\IntelliJ IDEA 2023.2.5\bin" -Dfile.encoding=UTF-8 -classpath E:\git-repository\multipleThread-master\multiplethread\target\classes;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-web\3.0.4\spring-boot-starter-web-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter\3.0.4\spring-boot-starter-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-logging\3.0.4\spring-boot-starter-logging-3.0.4.jar;C:\Users\miloq\.m2\repository\ch\qos\logback\logback-classic\1.4.5\logback-classic-1.4.5.jar;C:\Users\miloq\.m2\repository\ch\qos\logback\logback-core\1.4.5\logback-core-1.4.5.jar;C:\Users\miloq\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.19.0\log4j-to-slf4j-2.19.0.jar;C:\Users\miloq\.m2\repository\org\apache\logging\log4j\log4j-api\2.19.0\log4j-api-2.19.0.jar;C:\Users\miloq\.m2\repository\org\slf4j\jul-to-slf4j\2.0.6\jul-to-slf4j-2.0.6.jar;C:\Users\miloq\.m2\repository\org\yaml\snakeyaml\1.33\snakeyaml-1.33.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-json\3.0.4\spring-boot-starter-json-3.0.4.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.14.2\jackson-databind-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.14.2\jackson-annotations-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.14.2\jackson-core-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.14.2\jackson-datatype-jdk8-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.14.2\jackson-datatype-jsr310-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.14.2\jackson-module-parameter-names-2.14.2.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-web\6.0.6\spring-web-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-beans\6.0.6\spring-beans-6.0.6.jar;C:\Users\miloq\.m2\repository\io\micrometer\micrometer-observation\1.10.4\micrometer-observation-1.10.4.jar;C:\Users\miloq\.m2\repository\io\micrometer\micrometer-commons\1.10.4\micrometer-commons-1.10.4.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-webmvc\6.0.6\spring-webmvc-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-aop\6.0.6\spring-aop-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-context\6.0.6\spring-context-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-expression\6.0.6\spring-expression-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-devtools\3.0.4\spring-boot-devtools-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot\3.0.4\spring-boot-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\3.0.4\spring-boot-autoconfigure-3.0.4.jar;C:\Users\miloq\.m2\repository\org\projectlombok\lombok\1.18.26\lombok-1.18.26.jar;C:\Users\miloq\.m2\repository\jakarta\annotation\jakarta.annotation-api\2.1.1\jakarta.annotation-api-2.1.1.jar;C:\Users\miloq\.m2\repository\org\slf4j\slf4j-api\2.0.6\slf4j-api-2.0.6.jar;C:\Users\miloq\.m2\repository\org\hamcrest\hamcrest\2.2\hamcrest-2.2.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-core\6.0.6\spring-core-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-jcl\6.0.6\spring-jcl-6.0.6.jar;C:\Users\miloq\.m2\repository\com\alibaba\csp\sentinel-core\1.8.6\sentinel-core-1.8.6.jar;C:\Users\miloq\.m2\repository\com\lmax\disruptor\3.4.2\disruptor-3.4.2.jar;C:\Users\miloq\.m2\repository\org\openjdk\jol\jol-core\0.9\jol-core-0.9.jar;C:\Users\miloq\.m2\repository\com\alibaba\dubbo\2.5.3\dubbo-2.5.3.jar;C:\Users\miloq\.m2\repository\org\javassist\javassist\3.15.0-GA\javassist-3.15.0-GA.jar;C:\Users\miloq\.m2\repository\org\jboss\netty\netty\3.2.5.Final\netty-3.2.5.Final.jar;C:\Users\miloq\.m2\repository\junit\junit\4.13.2\junit-4.13.2.jar;C:\Users\miloq\.m2\repository\org\hamcrest\hamcrest-core\2.2\hamcrest-core-2.2.jar;C:\Users\miloq\.m2\repository\com\google\guava\guava\32.1.2-jre\guava-32.1.2-jre.jar;C:\Users\miloq\.m2\repository\com\google\guava\failureaccess\1.0.1\failureaccess-1.0.1.jar;C:\Users\miloq\.m2\repository\com\google\guava\listenablefuture\9999.0-empty-to-avoid-conflict-with-guava\listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar;C:\Users\miloq\.m2\repository\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;C:\Users\miloq\.m2\repository\org\checkerframework\checker-qual\3.33.0\checker-qual-3.33.0.jar;C:\Users\miloq\.m2\repository\com\google\errorprone\error_prone_annotations\2.18.0\error_prone_annotations-2.18.0.jar;C:\Users\miloq\.m2\repository\com\google\j2objc\j2objc-annotations\2.8\j2objc-annotations-2.8.jar;C:\Users\miloq\.m2\repository\org\apache\commons\commons-lang3\3.13.0\commons-lang3-3.13.0.jar imooc.atomic.lock.TestSync* # WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf* # WARNING: Unable to attach Serviceability Agent. sun.jvm.hotspot.memory.Universe.getNarrowOopBase()* java.lang.Object object internals:*  OFFSET  SIZE   TYPE DESCRIPTION                               VALUE*       0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)   - markword*       4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)   - markword*       8     4        (object header)                           58 0d 00 00 (01011000 00001101 00000000 00000000) (3416) - classpointer*      12     4        (loss due to the next object alignment)  - 对齐填充* Instance size: 16 bytes* Space losses: 0 bytes internal + 4 bytes external = 4 bytes total*** Process finished with exit code 0** 第一行、第二行都是markword* 第三行是classpointer* 因为没有数据,最后一行是对齐填充,markword和classpointer总共是12个byte,不能被8整除,所以对齐填充补充了4个byte*/System.out.println(ClassLayout.parseInstance(lock).toPrintable());
//        lock = new Order();
//        System.out.println(ClassLayout.parseInstance(lock).toPrintable());
//        synchronized (lock){
//            System.out.println(ClassLayout.parseInstance(lock).toPrintable());
//        }
//        Order o = (Order)lock;
//        o.setId(1);
//        System.out.println(ClassLayout.parseInstance(lock).toPrintable());}
}class Order{private int id;public int getId() {return id;}public void setId(int id) {this.id = id;}
}

现在将lock对象换成Order看一下会有什么变化?

package imooc.atomic.lock;import org.openjdk.jol.info.ClassLayout;/*** @Author: Alfred* @ModuleOwner: Alfred* @Description:*/
public class TestSync {public static void main(String[] args) {Object lock = new Object();/*** 使用ClassLayout,* 需要引入*      <dependency>* 			<groupId>org.openjdk.jol</groupId>* 			<artifactId>jol-core</artifactId>* 			<version>0.9</version>* 		</dependency>*//*** System.out.println(ClassLayout.parseInstance(lock).toPrintable());* 运行结果:* "C:\Program Files\Java\jdk-17.0.5\bin\java.exe" "-javaagent:C:\Program_Files\JetBrains\IntelliJ IDEA 2023.2.5\lib\idea_rt.jar=53687:C:\Program_Files\JetBrains\IntelliJ IDEA 2023.2.5\bin" -Dfile.encoding=UTF-8 -classpath E:\git-repository\multipleThread-master\multiplethread\target\classes;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-web\3.0.4\spring-boot-starter-web-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter\3.0.4\spring-boot-starter-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-logging\3.0.4\spring-boot-starter-logging-3.0.4.jar;C:\Users\miloq\.m2\repository\ch\qos\logback\logback-classic\1.4.5\logback-classic-1.4.5.jar;C:\Users\miloq\.m2\repository\ch\qos\logback\logback-core\1.4.5\logback-core-1.4.5.jar;C:\Users\miloq\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.19.0\log4j-to-slf4j-2.19.0.jar;C:\Users\miloq\.m2\repository\org\apache\logging\log4j\log4j-api\2.19.0\log4j-api-2.19.0.jar;C:\Users\miloq\.m2\repository\org\slf4j\jul-to-slf4j\2.0.6\jul-to-slf4j-2.0.6.jar;C:\Users\miloq\.m2\repository\org\yaml\snakeyaml\1.33\snakeyaml-1.33.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-json\3.0.4\spring-boot-starter-json-3.0.4.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.14.2\jackson-databind-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.14.2\jackson-annotations-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.14.2\jackson-core-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.14.2\jackson-datatype-jdk8-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.14.2\jackson-datatype-jsr310-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.14.2\jackson-module-parameter-names-2.14.2.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-web\6.0.6\spring-web-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-beans\6.0.6\spring-beans-6.0.6.jar;C:\Users\miloq\.m2\repository\io\micrometer\micrometer-observation\1.10.4\micrometer-observation-1.10.4.jar;C:\Users\miloq\.m2\repository\io\micrometer\micrometer-commons\1.10.4\micrometer-commons-1.10.4.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-webmvc\6.0.6\spring-webmvc-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-aop\6.0.6\spring-aop-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-context\6.0.6\spring-context-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-expression\6.0.6\spring-expression-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-devtools\3.0.4\spring-boot-devtools-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot\3.0.4\spring-boot-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\3.0.4\spring-boot-autoconfigure-3.0.4.jar;C:\Users\miloq\.m2\repository\org\projectlombok\lombok\1.18.26\lombok-1.18.26.jar;C:\Users\miloq\.m2\repository\jakarta\annotation\jakarta.annotation-api\2.1.1\jakarta.annotation-api-2.1.1.jar;C:\Users\miloq\.m2\repository\org\slf4j\slf4j-api\2.0.6\slf4j-api-2.0.6.jar;C:\Users\miloq\.m2\repository\org\hamcrest\hamcrest\2.2\hamcrest-2.2.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-core\6.0.6\spring-core-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-jcl\6.0.6\spring-jcl-6.0.6.jar;C:\Users\miloq\.m2\repository\com\alibaba\csp\sentinel-core\1.8.6\sentinel-core-1.8.6.jar;C:\Users\miloq\.m2\repository\com\lmax\disruptor\3.4.2\disruptor-3.4.2.jar;C:\Users\miloq\.m2\repository\org\openjdk\jol\jol-core\0.9\jol-core-0.9.jar;C:\Users\miloq\.m2\repository\com\alibaba\dubbo\2.5.3\dubbo-2.5.3.jar;C:\Users\miloq\.m2\repository\org\javassist\javassist\3.15.0-GA\javassist-3.15.0-GA.jar;C:\Users\miloq\.m2\repository\org\jboss\netty\netty\3.2.5.Final\netty-3.2.5.Final.jar;C:\Users\miloq\.m2\repository\junit\junit\4.13.2\junit-4.13.2.jar;C:\Users\miloq\.m2\repository\org\hamcrest\hamcrest-core\2.2\hamcrest-core-2.2.jar;C:\Users\miloq\.m2\repository\com\google\guava\guava\32.1.2-jre\guava-32.1.2-jre.jar;C:\Users\miloq\.m2\repository\com\google\guava\failureaccess\1.0.1\failureaccess-1.0.1.jar;C:\Users\miloq\.m2\repository\com\google\guava\listenablefuture\9999.0-empty-to-avoid-conflict-with-guava\listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar;C:\Users\miloq\.m2\repository\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;C:\Users\miloq\.m2\repository\org\checkerframework\checker-qual\3.33.0\checker-qual-3.33.0.jar;C:\Users\miloq\.m2\repository\com\google\errorprone\error_prone_annotations\2.18.0\error_prone_annotations-2.18.0.jar;C:\Users\miloq\.m2\repository\com\google\j2objc\j2objc-annotations\2.8\j2objc-annotations-2.8.jar;C:\Users\miloq\.m2\repository\org\apache\commons\commons-lang3\3.13.0\commons-lang3-3.13.0.jar imooc.atomic.lock.TestSync* # WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf* # WARNING: Unable to attach Serviceability Agent. sun.jvm.hotspot.memory.Universe.getNarrowOopBase()* java.lang.Object object internals:*  OFFSET  SIZE   TYPE DESCRIPTION                               VALUE*       0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)   - markword*       4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)   - markword*       8     4        (object header)                           58 0d 00 00 (01011000 00001101 00000000 00000000) (3416) - classpointer*      12     4        (loss due to the next object alignment)  - 对齐填充* Instance size: 16 bytes* Space losses: 0 bytes internal + 4 bytes external = 4 bytes total*** Process finished with exit code 0** 第一行、第二行都是markword* 第三行是classpointer* 因为没有数据,最后一行是对齐填充,markword和classpointer总共是12个byte,不能被8整除,所以对齐填充补充了4个byte*/System.out.println(ClassLayout.parseInstance(lock).toPrintable());lock = new Order();
//        System.out.println(ClassLayout.parseInstance(lock).toPrintable());synchronized (lock){System.out.println(ClassLayout.parseInstance(lock).toPrintable());}
//        Order o = (Order)lock;
//        o.setId(1);
//        System.out.println(ClassLayout.parseInstance(lock).toPrintable());}
}class Order{private int id;public int getId() {return id;}public void setId(int id) {this.id = id;}
}

运行结果:

"C:\Program Files\Java\jdk-17.0.5\bin\java.exe" "-javaagent:C:\Program_Files\JetBrains\IntelliJ IDEA 2023.2.5\lib\idea_rt.jar=55021:C:\Program_Files\JetBrains\IntelliJ IDEA 2023.2.5\bin" -Dfile.encoding=UTF-8 -classpath E:\git-repository\multipleThread-master\multiplethread\target\classes;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-web\3.0.4\spring-boot-starter-web-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter\3.0.4\spring-boot-starter-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-logging\3.0.4\spring-boot-starter-logging-3.0.4.jar;C:\Users\miloq\.m2\repository\ch\qos\logback\logback-classic\1.4.5\logback-classic-1.4.5.jar;C:\Users\miloq\.m2\repository\ch\qos\logback\logback-core\1.4.5\logback-core-1.4.5.jar;C:\Users\miloq\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.19.0\log4j-to-slf4j-2.19.0.jar;C:\Users\miloq\.m2\repository\org\apache\logging\log4j\log4j-api\2.19.0\log4j-api-2.19.0.jar;C:\Users\miloq\.m2\repository\org\slf4j\jul-to-slf4j\2.0.6\jul-to-slf4j-2.0.6.jar;C:\Users\miloq\.m2\repository\org\yaml\snakeyaml\1.33\snakeyaml-1.33.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-json\3.0.4\spring-boot-starter-json-3.0.4.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.14.2\jackson-databind-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.14.2\jackson-annotations-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.14.2\jackson-core-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.14.2\jackson-datatype-jdk8-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.14.2\jackson-datatype-jsr310-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.14.2\jackson-module-parameter-names-2.14.2.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-web\6.0.6\spring-web-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-beans\6.0.6\spring-beans-6.0.6.jar;C:\Users\miloq\.m2\repository\io\micrometer\micrometer-observation\1.10.4\micrometer-observation-1.10.4.jar;C:\Users\miloq\.m2\repository\io\micrometer\micrometer-commons\1.10.4\micrometer-commons-1.10.4.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-webmvc\6.0.6\spring-webmvc-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-aop\6.0.6\spring-aop-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-context\6.0.6\spring-context-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-expression\6.0.6\spring-expression-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-devtools\3.0.4\spring-boot-devtools-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot\3.0.4\spring-boot-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\3.0.4\spring-boot-autoconfigure-3.0.4.jar;C:\Users\miloq\.m2\repository\org\projectlombok\lombok\1.18.26\lombok-1.18.26.jar;C:\Users\miloq\.m2\repository\jakarta\annotation\jakarta.annotation-api\2.1.1\jakarta.annotation-api-2.1.1.jar;C:\Users\miloq\.m2\repository\org\slf4j\slf4j-api\2.0.6\slf4j-api-2.0.6.jar;C:\Users\miloq\.m2\repository\org\hamcrest\hamcrest\2.2\hamcrest-2.2.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-core\6.0.6\spring-core-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-jcl\6.0.6\spring-jcl-6.0.6.jar;C:\Users\miloq\.m2\repository\com\alibaba\csp\sentinel-core\1.8.6\sentinel-core-1.8.6.jar;C:\Users\miloq\.m2\repository\com\lmax\disruptor\3.4.2\disruptor-3.4.2.jar;C:\Users\miloq\.m2\repository\org\openjdk\jol\jol-core\0.9\jol-core-0.9.jar;C:\Users\miloq\.m2\repository\com\alibaba\dubbo\2.5.3\dubbo-2.5.3.jar;C:\Users\miloq\.m2\repository\org\javassist\javassist\3.15.0-GA\javassist-3.15.0-GA.jar;C:\Users\miloq\.m2\repository\org\jboss\netty\netty\3.2.5.Final\netty-3.2.5.Final.jar;C:\Users\miloq\.m2\repository\junit\junit\4.13.2\junit-4.13.2.jar;C:\Users\miloq\.m2\repository\org\hamcrest\hamcrest-core\2.2\hamcrest-core-2.2.jar;C:\Users\miloq\.m2\repository\com\google\guava\guava\32.1.2-jre\guava-32.1.2-jre.jar;C:\Users\miloq\.m2\repository\com\google\guava\failureaccess\1.0.1\failureaccess-1.0.1.jar;C:\Users\miloq\.m2\repository\com\google\guava\listenablefuture\9999.0-empty-to-avoid-conflict-with-guava\listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar;C:\Users\miloq\.m2\repository\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;C:\Users\miloq\.m2\repository\org\checkerframework\checker-qual\3.33.0\checker-qual-3.33.0.jar;C:\Users\miloq\.m2\repository\com\google\errorprone\error_prone_annotations\2.18.0\error_prone_annotations-2.18.0.jar;C:\Users\miloq\.m2\repository\com\google\j2objc\j2objc-annotations\2.8\j2objc-annotations-2.8.jar;C:\Users\miloq\.m2\repository\org\apache\commons\commons-lang3\3.13.0\commons-lang3-3.13.0.jar imooc.atomic.lock.TestSync
# WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf
# WARNING: Unable to attach Serviceability Agent. sun.jvm.hotspot.memory.Universe.getNarrowOopBase()
java.lang.Object object internals:OFFSET  SIZE   TYPE DESCRIPTION                               VALUE0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4        (object header)                           58 0d 00 00 (01011000 00001101 00000000 00000000) (3416)12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes totalimooc.atomic.lock.Order object internals:OFFSET  SIZE   TYPE DESCRIPTION                               VALUE0     4        (object header)                           00 f4 bf ef (00000000 11110100 10111111 11101111) (-272632832)4     4        (object header)                           0e 00 00 00 (00001110 00000000 00000000 00000000) (14)8     4        (object header)                           08 d6 c1 00 (00001000 11010110 11000001 00000000) (12703240)12     4    int Order.id                                  0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes totalProcess finished with exit code 0

可以看到,markword发生了变化,指向了Monitor对象的一个指针。

现在给Order的id赋值,看下有什么变化?

package imooc.atomic.lock;import org.openjdk.jol.info.ClassLayout;/*** @Author: Alfred* @ModuleOwner: Alfred* @Description:*/
public class TestSync {public static void main(String[] args) {Object lock = new Object();/*** 使用ClassLayout,* 需要引入*      <dependency>* 			<groupId>org.openjdk.jol</groupId>* 			<artifactId>jol-core</artifactId>* 			<version>0.9</version>* 		</dependency>*//*** System.out.println(ClassLayout.parseInstance(lock).toPrintable());* 运行结果:* "C:\Program Files\Java\jdk-17.0.5\bin\java.exe" "-javaagent:C:\Program_Files\JetBrains\IntelliJ IDEA 2023.2.5\lib\idea_rt.jar=53687:C:\Program_Files\JetBrains\IntelliJ IDEA 2023.2.5\bin" -Dfile.encoding=UTF-8 -classpath E:\git-repository\multipleThread-master\multiplethread\target\classes;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-web\3.0.4\spring-boot-starter-web-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter\3.0.4\spring-boot-starter-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-logging\3.0.4\spring-boot-starter-logging-3.0.4.jar;C:\Users\miloq\.m2\repository\ch\qos\logback\logback-classic\1.4.5\logback-classic-1.4.5.jar;C:\Users\miloq\.m2\repository\ch\qos\logback\logback-core\1.4.5\logback-core-1.4.5.jar;C:\Users\miloq\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.19.0\log4j-to-slf4j-2.19.0.jar;C:\Users\miloq\.m2\repository\org\apache\logging\log4j\log4j-api\2.19.0\log4j-api-2.19.0.jar;C:\Users\miloq\.m2\repository\org\slf4j\jul-to-slf4j\2.0.6\jul-to-slf4j-2.0.6.jar;C:\Users\miloq\.m2\repository\org\yaml\snakeyaml\1.33\snakeyaml-1.33.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-json\3.0.4\spring-boot-starter-json-3.0.4.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.14.2\jackson-databind-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.14.2\jackson-annotations-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.14.2\jackson-core-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.14.2\jackson-datatype-jdk8-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.14.2\jackson-datatype-jsr310-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.14.2\jackson-module-parameter-names-2.14.2.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-web\6.0.6\spring-web-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-beans\6.0.6\spring-beans-6.0.6.jar;C:\Users\miloq\.m2\repository\io\micrometer\micrometer-observation\1.10.4\micrometer-observation-1.10.4.jar;C:\Users\miloq\.m2\repository\io\micrometer\micrometer-commons\1.10.4\micrometer-commons-1.10.4.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-webmvc\6.0.6\spring-webmvc-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-aop\6.0.6\spring-aop-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-context\6.0.6\spring-context-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-expression\6.0.6\spring-expression-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-devtools\3.0.4\spring-boot-devtools-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot\3.0.4\spring-boot-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\3.0.4\spring-boot-autoconfigure-3.0.4.jar;C:\Users\miloq\.m2\repository\org\projectlombok\lombok\1.18.26\lombok-1.18.26.jar;C:\Users\miloq\.m2\repository\jakarta\annotation\jakarta.annotation-api\2.1.1\jakarta.annotation-api-2.1.1.jar;C:\Users\miloq\.m2\repository\org\slf4j\slf4j-api\2.0.6\slf4j-api-2.0.6.jar;C:\Users\miloq\.m2\repository\org\hamcrest\hamcrest\2.2\hamcrest-2.2.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-core\6.0.6\spring-core-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-jcl\6.0.6\spring-jcl-6.0.6.jar;C:\Users\miloq\.m2\repository\com\alibaba\csp\sentinel-core\1.8.6\sentinel-core-1.8.6.jar;C:\Users\miloq\.m2\repository\com\lmax\disruptor\3.4.2\disruptor-3.4.2.jar;C:\Users\miloq\.m2\repository\org\openjdk\jol\jol-core\0.9\jol-core-0.9.jar;C:\Users\miloq\.m2\repository\com\alibaba\dubbo\2.5.3\dubbo-2.5.3.jar;C:\Users\miloq\.m2\repository\org\javassist\javassist\3.15.0-GA\javassist-3.15.0-GA.jar;C:\Users\miloq\.m2\repository\org\jboss\netty\netty\3.2.5.Final\netty-3.2.5.Final.jar;C:\Users\miloq\.m2\repository\junit\junit\4.13.2\junit-4.13.2.jar;C:\Users\miloq\.m2\repository\org\hamcrest\hamcrest-core\2.2\hamcrest-core-2.2.jar;C:\Users\miloq\.m2\repository\com\google\guava\guava\32.1.2-jre\guava-32.1.2-jre.jar;C:\Users\miloq\.m2\repository\com\google\guava\failureaccess\1.0.1\failureaccess-1.0.1.jar;C:\Users\miloq\.m2\repository\com\google\guava\listenablefuture\9999.0-empty-to-avoid-conflict-with-guava\listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar;C:\Users\miloq\.m2\repository\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;C:\Users\miloq\.m2\repository\org\checkerframework\checker-qual\3.33.0\checker-qual-3.33.0.jar;C:\Users\miloq\.m2\repository\com\google\errorprone\error_prone_annotations\2.18.0\error_prone_annotations-2.18.0.jar;C:\Users\miloq\.m2\repository\com\google\j2objc\j2objc-annotations\2.8\j2objc-annotations-2.8.jar;C:\Users\miloq\.m2\repository\org\apache\commons\commons-lang3\3.13.0\commons-lang3-3.13.0.jar imooc.atomic.lock.TestSync* # WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf* # WARNING: Unable to attach Serviceability Agent. sun.jvm.hotspot.memory.Universe.getNarrowOopBase()* java.lang.Object object internals:*  OFFSET  SIZE   TYPE DESCRIPTION                               VALUE*       0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)   - markword*       4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)   - markword*       8     4        (object header)                           58 0d 00 00 (01011000 00001101 00000000 00000000) (3416) - classpointer*      12     4        (loss due to the next object alignment)  - 对齐填充* Instance size: 16 bytes* Space losses: 0 bytes internal + 4 bytes external = 4 bytes total*** Process finished with exit code 0** 第一行、第二行都是markword* 第三行是classpointer* 因为没有数据,最后一行是对齐填充,markword和classpointer总共是12个byte,不能被8整除,所以对齐填充补充了4个byte*/System.out.println(ClassLayout.parseInstance(lock).toPrintable());lock = new Order();
//        System.out.println(ClassLayout.parseInstance(lock).toPrintable());
//        synchronized (lock){
//            System.out.println(ClassLayout.parseInstance(lock).toPrintable());
//        }Order o = (Order)lock;o.setId(1);System.out.println(ClassLayout.parseInstance(lock).toPrintable());}
}class Order{private int id;public int getId() {return id;}public void setId(int id) {this.id = id;}
}

运行结果:

解释:

        线程1尝试获取锁,如果获取到了锁,会把自己设置为Monitor对象的owner变量,代表Thread1线程正在执行,如果其他线程来获取锁,就获取不到锁,会进入到EnterList里边,如果某个线程在执行的时候,调用了join()或wait()方法,那么线程会进入到Monitor对象的WaitSet里,当一个线程执行完毕,退出并释放Monitor锁。

解释:

        java对象里的markword的指针,指向的就是对象关联的Monitor。

通过运行并反编译下面代码查看:

package imooc.atomic.lock;import org.openjdk.jol.info.ClassLayout;/*** @Author: Alfred* @ModuleOwner: Alfred* @Description:*/
public class TestSync {public static void main(String[] args) {Object lock = new Object();/*** 使用ClassLayout,* 需要引入*      <dependency>* 			<groupId>org.openjdk.jol</groupId>* 			<artifactId>jol-core</artifactId>* 			<version>0.9</version>* 		</dependency>*//*** System.out.println(ClassLayout.parseInstance(lock).toPrintable());* 运行结果:* "C:\Program Files\Java\jdk-17.0.5\bin\java.exe" "-javaagent:C:\Program_Files\JetBrains\IntelliJ IDEA 2023.2.5\lib\idea_rt.jar=53687:C:\Program_Files\JetBrains\IntelliJ IDEA 2023.2.5\bin" -Dfile.encoding=UTF-8 -classpath E:\git-repository\multipleThread-master\multiplethread\target\classes;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-web\3.0.4\spring-boot-starter-web-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter\3.0.4\spring-boot-starter-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-logging\3.0.4\spring-boot-starter-logging-3.0.4.jar;C:\Users\miloq\.m2\repository\ch\qos\logback\logback-classic\1.4.5\logback-classic-1.4.5.jar;C:\Users\miloq\.m2\repository\ch\qos\logback\logback-core\1.4.5\logback-core-1.4.5.jar;C:\Users\miloq\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.19.0\log4j-to-slf4j-2.19.0.jar;C:\Users\miloq\.m2\repository\org\apache\logging\log4j\log4j-api\2.19.0\log4j-api-2.19.0.jar;C:\Users\miloq\.m2\repository\org\slf4j\jul-to-slf4j\2.0.6\jul-to-slf4j-2.0.6.jar;C:\Users\miloq\.m2\repository\org\yaml\snakeyaml\1.33\snakeyaml-1.33.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-starter-json\3.0.4\spring-boot-starter-json-3.0.4.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.14.2\jackson-databind-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.14.2\jackson-annotations-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.14.2\jackson-core-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.14.2\jackson-datatype-jdk8-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.14.2\jackson-datatype-jsr310-2.14.2.jar;C:\Users\miloq\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.14.2\jackson-module-parameter-names-2.14.2.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-web\6.0.6\spring-web-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-beans\6.0.6\spring-beans-6.0.6.jar;C:\Users\miloq\.m2\repository\io\micrometer\micrometer-observation\1.10.4\micrometer-observation-1.10.4.jar;C:\Users\miloq\.m2\repository\io\micrometer\micrometer-commons\1.10.4\micrometer-commons-1.10.4.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-webmvc\6.0.6\spring-webmvc-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-aop\6.0.6\spring-aop-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-context\6.0.6\spring-context-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-expression\6.0.6\spring-expression-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-devtools\3.0.4\spring-boot-devtools-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot\3.0.4\spring-boot-3.0.4.jar;C:\Users\miloq\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\3.0.4\spring-boot-autoconfigure-3.0.4.jar;C:\Users\miloq\.m2\repository\org\projectlombok\lombok\1.18.26\lombok-1.18.26.jar;C:\Users\miloq\.m2\repository\jakarta\annotation\jakarta.annotation-api\2.1.1\jakarta.annotation-api-2.1.1.jar;C:\Users\miloq\.m2\repository\org\slf4j\slf4j-api\2.0.6\slf4j-api-2.0.6.jar;C:\Users\miloq\.m2\repository\org\hamcrest\hamcrest\2.2\hamcrest-2.2.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-core\6.0.6\spring-core-6.0.6.jar;C:\Users\miloq\.m2\repository\org\springframework\spring-jcl\6.0.6\spring-jcl-6.0.6.jar;C:\Users\miloq\.m2\repository\com\alibaba\csp\sentinel-core\1.8.6\sentinel-core-1.8.6.jar;C:\Users\miloq\.m2\repository\com\lmax\disruptor\3.4.2\disruptor-3.4.2.jar;C:\Users\miloq\.m2\repository\org\openjdk\jol\jol-core\0.9\jol-core-0.9.jar;C:\Users\miloq\.m2\repository\com\alibaba\dubbo\2.5.3\dubbo-2.5.3.jar;C:\Users\miloq\.m2\repository\org\javassist\javassist\3.15.0-GA\javassist-3.15.0-GA.jar;C:\Users\miloq\.m2\repository\org\jboss\netty\netty\3.2.5.Final\netty-3.2.5.Final.jar;C:\Users\miloq\.m2\repository\junit\junit\4.13.2\junit-4.13.2.jar;C:\Users\miloq\.m2\repository\org\hamcrest\hamcrest-core\2.2\hamcrest-core-2.2.jar;C:\Users\miloq\.m2\repository\com\google\guava\guava\32.1.2-jre\guava-32.1.2-jre.jar;C:\Users\miloq\.m2\repository\com\google\guava\failureaccess\1.0.1\failureaccess-1.0.1.jar;C:\Users\miloq\.m2\repository\com\google\guava\listenablefuture\9999.0-empty-to-avoid-conflict-with-guava\listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar;C:\Users\miloq\.m2\repository\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;C:\Users\miloq\.m2\repository\org\checkerframework\checker-qual\3.33.0\checker-qual-3.33.0.jar;C:\Users\miloq\.m2\repository\com\google\errorprone\error_prone_annotations\2.18.0\error_prone_annotations-2.18.0.jar;C:\Users\miloq\.m2\repository\com\google\j2objc\j2objc-annotations\2.8\j2objc-annotations-2.8.jar;C:\Users\miloq\.m2\repository\org\apache\commons\commons-lang3\3.13.0\commons-lang3-3.13.0.jar imooc.atomic.lock.TestSync* # WARNING: Unable to get Instrumentation. Dynamic Attach failed. You may add this JAR as -javaagent manually, or supply -Djdk.attach.allowAttachSelf* # WARNING: Unable to attach Serviceability Agent. sun.jvm.hotspot.memory.Universe.getNarrowOopBase()* java.lang.Object object internals:*  OFFSET  SIZE   TYPE DESCRIPTION                               VALUE*       0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)   - markword*       4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)   - markword*       8     4        (object header)                           58 0d 00 00 (01011000 00001101 00000000 00000000) (3416) - classpointer*      12     4        (loss due to the next object alignment)  - 对齐填充* Instance size: 16 bytes* Space losses: 0 bytes internal + 4 bytes external = 4 bytes total*** Process finished with exit code 0** 第一行、第二行都是markword* 第三行是classpointer* 因为没有数据,最后一行是对齐填充,markword和classpointer总共是12个byte,不能被8整除,所以对齐填充补充了4个byte*/
//        System.out.println(ClassLayout.parseInstance(lock).toPrintable());
//        lock = new Order();
//        System.out.println(ClassLayout.parseInstance(lock).toPrintable());synchronized (lock){
//            System.out.println(ClassLayout.parseInstance(lock).toPrintable());}
//        Order o = (Order)lock;
//        o.setId(1);
//        System.out.println(ClassLayout.parseInstance(lock).toPrintable());}
}class Order{private int id;public int getId() {return id;}public void setId(int id) {this.id = id;}
}

java虚拟机对应代码:

ObjectMonitor对象:

这里就有我们刚才提到的owner、WaitSet以及EntryList。

现在回到前面的inflake方法处:

enter方法:

 

如果设置失败了,会调用EnterI():

EnterI()就是把自己线程放到EnterList里边。 

8.如何实现锁升级?

上卫生间,给卫生间上锁,这就是使用重量级锁的过程。

但是,如果屋子里只有自己一个人,上卫生间,这个时候再给卫生间门上加把锁,就是非常麻烦了。

如果房间里还有其他人,他可以给卫生间门锁贴个字条,某人专用,这个就像是偏向锁。

现在有两人同时要上卫生间,两个人进去的条件是卫生间没人。

A要进去的时候,卫生间门口牌子上没有写有人在使用,A就在门口牌子写上自己的名字,并进去了。

B要进去的时候,发现卫生间门口牌子上写有A的名字,B这个时候就不能进去了。

为了节省空间,有些位置是公用的,

因为位置是公用的,无锁状态31bit的hashcode,在转为偏向锁后54bit的threadId是冲突的,所以计算过hashcode,偏向线程ID和当前线程ID不一致,则当前线程无法进入。

 大白话偏向锁原理:

java 偏向锁原理
Java 偏向锁(Biased Locking)是 Java 6 引入的一项优化技术,旨在减少无竞争同步的同步开销。这项技术主要在 HotSpot JVM 中实现,并被用在锁的内部实现中,以提高多线程环境下程序的性能。偏向锁的引入是为了解决轻量级锁带来的性能问题,特别是在大多数情况下没有竞争发生的场景下。偏向锁的工作原理
检测锁状态:当一个线程访问同步代码块时,首先会检查锁对象的状态。如果锁对象处于无锁状态(未被任何线程锁定),那么偏向锁机制就会尝试将这个锁对象的所有权偏向于第一个访问该同步块的线程。偏向设置:当第一个线程访问同步块时,JVM 会将对象头中的 Mark Word 设置为偏向模式,并且使用 CAS 操作将偏向的线程 ID 记录到对象的 Mark Word 中。这样,这个线程在接下来的执行过程中就可以快速地进入同步块,而不需要再进行重量级的同步操作。撤销偏向:如果有第二个线程尝试进入这个同步块,JVM 会检测到 Mark Word 中的偏向模式,并且发现偏向的线程 ID 与当前线程不匹配。这时,JVM 会撤销偏向模式,恢复到无锁状态或者升级为轻量级锁(如果检测到有竞争发生)。这个过程称为“撤销偏向”(Revoking Bias)。轻量级锁到重量级锁的升级:如果在运行过程中检测到多个线程竞争同一个锁对象,JVM 会将这些轻量级锁升级为重量级锁(使用操作系统级别的互斥量),以支持多线程之间的竞争。偏向锁的优势
减少延迟:在大多数情况下,只有一个线程访问同步块的场景中,偏向锁可以显著减少线程获取锁的开销。降低开销:相比于轻量级锁和重量级锁,偏向锁的开销更小,因为偏向模式只需要在第一次访问时设置一次。偏向锁的适用场景
单线程环境:在只有一个线程频繁访问同步块的场景中,偏向锁可以提供更好的性能。多线程低竞争环境:在多线程但竞争不激烈的情况下,偏向锁可以减少线程间的切换和同步开销。注意事项
撤销偏向的开销:虽然偏向锁可以带来性能优势,但撤销偏向的开销(尤其是在高竞争环境下)可能会抵消其优势。因此,JVM 提供了一个参数 -XX:-UseBiasedLocking 来禁用偏向锁。动态检测:JVM 会动态地监控锁的使用情况,如果检测到某个锁长时间不被撤销偏向,JVM 可能会自动关闭偏向锁功能以避免不必要的开销。通过理解和合理使用偏向锁,可以在 Java 应用程序中获得更好的性能表现。

新来的C想使用卫生间,看到卫生间门牌写着A专用,现在要把卫生间的门牌去掉,取消A的专用状态,这就是偏向锁的解锁,恢复到无锁状态。

取消掉门牌后,C一直围着卫生间转圈,等待A从卫生间出来,这就是轻量级锁,轻量级锁是相对于重量级锁来说的。

LockRecord是每个线程独有的,将锁对象的markword复制到每个线程的LockRecord里面,然后都尝试,把锁对象的markword替换为指向自己线程的LockRecord的指针,哪个线程替换成功了,哪个线程就获取到了这个线程的轻量级锁。

那为什么要把锁对象的markword替换到线程的LockRecord中,再用CAS操作更新指针,不能直接把锁对象的markword直接更新为线程自己ide指针吗?

轻量级锁加锁时,中间有62bit,存储的是LockRecord指针,如果直接使用CAS替换为指针,那锁对象的hashcode、分代年龄等这些信息就丢失了,所以需要找一个地方暂存起来,比如,轻量级锁拿到后,调用hashcode,就需要根据存储在锁对象的LockRecord指针,找到对象的LockRecord,再从LockRecord中获取锁对象的hashcode,锁标志位00,代表的是轻量级锁。

解释:

        轻量级锁的解锁过程,就是反向操作,将线程对象的LockRecord中的markword替换回锁对象的过程。

自适应自旋锁:

        自适应意味着自旋的时间不再是固定的,而是由上一次在同一个锁上自旋的时间以及锁的拥有者的状态来决定的。

        如果在同一个对象上,刚刚成功获得锁,并且持有锁的线程正在运行中,那么JVM就会认为这次自旋很大可能也会成功,那么JVM会选择更多次数的自旋。

        如果对于某个锁,自旋很少成功获得过,可以直接省掉自旋的过程,以避免浪费处理器资源。

参考:java中自旋详细介绍_java 自旋锁-CSDN博客

重量级锁,就是一开始提到的Monitor。

轻量级锁调用了 wait 与 notify 方法后还是轻量级锁吗?

在Java中,轻量级锁(Thin Lock)是一种优化机制,用于减少多线程竞争时的开销。然而,一旦调用了wait()notify()方法,锁的状态会发生变化。

具体来说:

  1. 调用wait()方法:当一个线程调用wait()时,它会释放持有的锁,并进入等待状态。此时,锁会从轻量级锁升级为重量级锁(Heavyweight Lock),因为wait()notify()机制依赖于底层的监视器(Monitor),而监视器的实现通常涉及操作系统级别的资源管理,这会增加锁的开销。

  2. 调用notify()notifyAll()方法:这些方法用于唤醒等待中的线程。虽然它们本身不会直接改变锁的状态,但由于它们通常与wait()方法配合使用,锁的状态可能已经因为wait()的调用而升级为重量级锁。

因此,调用了wait()notify()方法后,锁通常不再是轻量级锁,而是会升级为重量级锁。这种升级是为了支持更复杂的线程同步机制,确保线程能够正确地进行等待和唤醒操作。

9.什么场景下使用Java提供的原子类AtomicInteger保证原子类?

package imooc.atomic;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;public class TestAtomicInteger {public static void main(String[] args) throws InterruptedException {int count = 0;count++;AtomicInteger atomicInteger = new AtomicInteger(5);System.out.println(atomicInteger.getAndIncrement());//i++,返回的是原始值5System.out.println(atomicInteger.get());//返回操作后(即现在)的值System.out.println(atomicInteger.incrementAndGet());//++i, 返回++i之后的值System.out.println(atomicInteger.decrementAndGet());//--iSystem.out.println(atomicInteger.getAndDecrement());//i--System.out.println(atomicInteger.get());//返回操作后(即现在)的值
//        AtomicInteger count = new AtomicInteger();
//        List<Thread> list = new ArrayList<>();
//        for (int i = 0; i < 100; i++) {
//            Thread thread = new Thread(() -> {
//                for (int j = 0; j < 100; j++) {
//                    count.incrementAndGet();
//                }
//            });
//            list.add(thread);
//        }
//        for (Thread thread : list) {
//            thread.start();
//        }
//        for (Thread thread : list) {
//            thread.join();
//        }
//        System.out.println(count.get());}
}

运行结果:

下面多线程自增测试下AtomicInteger:

package imooc.atomic;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;public class TestAtomicInteger {public static void main(String[] args) throws InterruptedException {
//        int count = 0;
//        count++;
//        AtomicInteger atomicInteger = new AtomicInteger(5);
//        System.out.println(atomicInteger.getAndIncrement());//i++,返回的是原始值5
//        System.out.println(atomicInteger.get());//返回操作后(即现在)的值
//        System.out.println(atomicInteger.incrementAndGet());//++i, 返回++i之后的值
//        System.out.println(atomicInteger.decrementAndGet());//--i
//        System.out.println(atomicInteger.getAndDecrement());//i--
//        System.out.println(atomicInteger.get());//返回操作后(即现在)的值AtomicInteger count = new AtomicInteger();List<Thread> list = new ArrayList<>();for (int i = 0; i < 100; i++) {Thread thread = new Thread(() -> {for (int j = 0; j < 100; j++) {count.incrementAndGet();}});list.add(thread);}//批量启动线程for (Thread thread : list) {thread.start();}//确保所有线程都运行结束,再进行控制台打印输出for (Thread thread : list) {thread.join();}System.out.println(count.get());}
}

运行结果:

package imooc.atomic;import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntUnaryOperator;public class TestAtomicInteger2 {public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger(5);//初始值5
//        atomicInteger.getAndAdd(10);//先返回再加10
//        System.out.println(atomicInteger);
//        System.out.println(atomicInteger.addAndGet(5));//先加再返回int i = updateAndGet(atomicInteger);
//        int i = updateAndGet(atomicInteger,x->x/5);
//        int i = atomicInteger.updateAndGet(x -> x * 5);System.out.println(i);}public static int updateAndGet(AtomicInteger atomicInteger){while (true){int cur = atomicInteger.get();int next = cur * 5;
//            int next = intUnaryOperator.applyAsInt(cur);/*** Compare-and-Swap (CAS) 是另一种常见的原子指令,* 它允许 CPU 在一次操作中比较内存中的值并与预期值进行对比,如果匹配,则更新内存中的值;如果不匹配,则返回当前值* cur 当前值* next 待更新的值*/if(atomicInteger.compareAndSet(cur,next)){return next;}}}
}

运行结果:

但是这么写不太通用,稍加改造:

package imooc.atomic;import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntUnaryOperator;public class TestAtomicInteger2 {public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger(5);//初始值5
//        atomicInteger.getAndAdd(10);//先返回再加10
//        System.out.println(atomicInteger);
//        System.out.println(atomicInteger.addAndGet(5));//先加再返回//        int i = updateAndGet(atomicInteger);int i = updateAndGet(atomicInteger,x->x/5);
//        int i = atomicInteger.updateAndGet(x -> x * 5);System.out.println(i);}/**** @param atomicInteger* @param intUnaryOperator Java1.8引入的函数式变成,提供一个函数式表达式进行计算* @return*/public static int updateAndGet(AtomicInteger atomicInteger, IntUnaryOperator intUnaryOperator){while (true){int cur = atomicInteger.get();
//            int next = cur * 5;int next = intUnaryOperator.applyAsInt(cur);/*** Compare-and-Swap (CAS) 是另一种常见的原子指令,* 它允许 CPU 在一次操作中比较内存中的值并与预期值进行对比,如果匹配,则更新内存中的值;如果不匹配,则返回当前值* cur 当前值* next 待更新的值*/if(atomicInteger.compareAndSet(cur,next)){return next;}}}
}
运行结果:
IntUnaryOperator类

参考:Java函数式IntUnaryOperator接口介绍、应用场景和示例代码_java intunaryoperator-CSDN博客

如果担心自己写的方法,写错了,可以直接使用JDK提供的方法updateAndGet():

package imooc.atomic;import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntUnaryOperator;public class TestAtomicInteger2 {public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger(5);//初始值5
//        atomicInteger.getAndAdd(10);//先返回再加10
//        System.out.println(atomicInteger);
//        System.out.println(atomicInteger.addAndGet(5));//先加再返回//        int i = updateAndGet(atomicInteger);//自己实现的方法
//        int i = updateAndGet(atomicInteger,x->x/5);//JDK提供的方法int i = atomicInteger.updateAndGet(x -> x * 5);System.out.println(i);}/**** @param atomicInteger* @param intUnaryOperator Java1.8引入的函数式变成,提供一个函数式表达式进行计算* @return*/public static int updateAndGet(AtomicInteger atomicInteger, IntUnaryOperator intUnaryOperator){while (true){int cur = atomicInteger.get();
//            int next = cur * 5;int next = intUnaryOperator.applyAsInt(cur);/*** Compare-and-Swap (CAS) 是另一种常见的原子指令,* 它允许 CPU 在一次操作中比较内存中的值并与预期值进行对比,如果匹配,则更新内存中的值;如果不匹配,则返回当前值* cur 当前值* next 待更新的值*/if(atomicInteger.compareAndSet(cur,next)){return next;}}}
}

解释:

        1.AtomicInteger只能保证一个变量的原子性,如果要保证多个变量之间复合操作的原子性还是要使用锁;

        2.ABA问题 - 初值1,被别的线程更新成0,又更新成1,但是此1非彼1,这种情况不能使用Atomic;

        3.竞争非常激烈,自旋时间可能过长- CAS要在循环中不断更新这个值,自旋时间可能会很长。

为什么竞争过于激烈的场景,为什么不能使用AtomicInteger

在竞争过于激烈的场景中,使用 `AtomicInteger` 可能会遇到性能问题,原因如下:1. **CAS 自旋开销**:- `AtomicInteger` 依赖 CAS(Compare-And-Swap)操作,在高并发下,多个线程可能反复尝试更新同一个变量,导致大量线程处于自旋状态,增加 CPU 开销,降低性能。2. **缓存一致性压力**:- 频繁的 CAS 操作会导致缓存行在多核 CPU 之间频繁失效和同步,增加缓存一致性协议的开销,进一步影响性能。3. **伪共享**:- 如果多个 `AtomicInteger` 实例存储在相邻内存位置,一个实例的更新可能导致其他实例的缓存行失效,即使它们没有被修改,这会增加不必要的缓存同步开销。4. **线程争用**:- 高并发下,多个线程争用同一个 `AtomicInteger` 实例,可能导致某些线程长时间无法成功更新,增加等待时间,降低吞吐量。### 替代方案
在竞争激烈的场景中,可以考虑以下替代方案:1. **LongAdder**:- `LongAdder` 通过分段计数减少争用,适合高并发场景,性能优于 `AtomicInteger`。2. **ConcurrentHashMap**:- 如果需要维护多个计数器,可以使用 `ConcurrentHashMap`,每个线程更新不同的键,减少争用。3. **锁**:- 在某些情况下,使用锁(如 `ReentrantLock`)可能比 CAS 更高效,尤其是在临界区操作较复杂时。### 总结
在竞争激烈的场景中,`AtomicInteger` 的 CAS 操作可能导致性能下降。选择 `LongAdder` 或 `ConcurrentHashMap` 等替代方案,可以有效减少争用,提升性能。

10.原子类是如何利用CAS(比较并交换)保证线程安全的

 

不同的操作系统实现不同:

参考linux_x86:

解释:

        1.如果是单核的,就不加lock指令,如果是多核CPU,需要加lock指令后,调用cmpxchgg汇编指令;

        2.1中提到的lock指令,有什么作用呢,可以参考Inter架构的操作手册

可以看到就是锁总线。

总结:使用CAS可以提高并发效率,但是并不一定会比synchronized效率更高

11.高并发下AtomicInteger的性能 VS synchronized的性能

package imooc.atomic;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;public class CompareLockAndAtomic {public static void main(String[] args) throws InterruptedException {//写一个方法,传入线程数,分别测试不同线程数的情况下atomoic和synchronized的运行速度testLock(100);testAtomic(100);testLock(1000);testAtomic(1000);testLock(3000);testAtomic(3000);}private static void testAtomic(int threadNum) throws InterruptedException {long startTime = System.currentTimeMillis();AtomicInteger atomicInteger = new AtomicInteger();List<Thread> list = new ArrayList<>();for (int i = 0; i < threadNum; i++) {list.add(new Thread(()->{for (int j = 0; j < 3000; j++) {atomicInteger.incrementAndGet();}}));}for (Thread thread : list) {thread.start();}for (Thread thread : list) {thread.join();}long endTime = System.currentTimeMillis();System.out.println("atomic用时 "+(endTime-startTime)+" count :"+atomicInteger.get()+" 线程数 :"+threadNum);}private static void testLock(int threadNum) throws InterruptedException {long startTime = System.currentTimeMillis();List<Thread> list = new ArrayList<>();for (int i = 0; i < threadNum; i++) {Thread thread = new IncrementThread();list.add(thread);}for (Thread thread : list) {thread.start();}for (Thread thread : list) {thread.join();}long endTime = System.currentTimeMillis();System.out.println("lock用时 "+(endTime-startTime)+" count :"+IncrementThread.count+" 线程数 :"+threadNum);IncrementThread.count=0;}
}
class IncrementThread extends Thread{static int count = 0;@Overridepublic void run() {for (int i = 0; i < 3000; i++) {synchronized (IncrementThread.class){count++;}}}
}

运行结果:

结论:

        在高并发场景下,使用synchroized可能比Atomic效率更高。

12.高并发下AtomicBoolean和AtomicLong的用法

AtomlicBoolean - 一般在框架里用的比较多

使用示例:

package imooc.atomic;import java.util.concurrent.atomic.AtomicBoolean;public class TestAtomicBoolean {public static void main(String[] args) {AtomicBoolean atomicBoolean = new AtomicBoolean(false);System.out.println(atomicBoolean.getAndSet(true));//falseSystem.out.println(atomicBoolean.get());//true}
}

运行结果:

AtomicLong使用方法: 与AtomicInteger类似

package imooc.atomic;import java.util.concurrent.atomic.AtomicLong;public class TestAtomicLong {public static void main(String[] args) {AtomicLong atomicLong = new AtomicLong(5);
//        System.out.println(atomicLong.getAndSet(6));//5  先get再赋值
//        System.out.println(atomicLong.get());//6 获取操作后(即现在)的值
//        System.out.println(atomicLong.incrementAndGet());//7 先自增再get
//        System.out.println(atomicLong.getAndIncrement());//7 先get再自增
//        System.out.println(atomicLong.get());//8  获取操作后(即现在)的值while (true){long pre = atomicLong.get();long next =  pre+5;if(atomicLong.compareAndSet(pre,next)) {break;}}System.out.println(atomicLong.get());}
}

 13.高并发下AtomicIntegerArray和AtomicReference的用法

使用示例:

package imooc.atomic;import java.util.concurrent.atomic.AtomicIntegerArray;public class TestAtomicIntegerArray {public static void main(String[] args) {//构造一个长度为10的原子整形数组AtomicIntegerArray array = new AtomicIntegerArray(10);
//        System.out.println(array.addAndGet(0, 5)); //更新第0个元素,更新值为5while (true){int pre = array.get(5); //获取原子整形数组第5个元素int next = pre+10;if(array.compareAndSet(5,pre,next)){break;}}System.out.println(array.get(5));}
}

运行结果:

 

使用示例:

package imooc.atomic;import java.util.concurrent.atomic.AtomicReference;public class TestAtomicReference {public static void main(String[] args) {//如果AtomicReference定义的类型是Integer,那其实就是和AtomicInteger一样的类
//        AtomicReference<Integer> reference = new AtomicReference<>();Order order = new Order("1","1");AtomicReference<Order> reference = new AtomicReference<>(order);Order order1 = new Order("2","2");System.out.println(reference.getAndSet(order1));System.out.println(reference.get());while (true){Order order2  = reference.get();String id = order2.getId();String name = order2.getName();id = id+"end";name = name +"end";Order order3 = new Order(id,name);if(reference.compareAndSet(order1,order3)){break;}}System.out.println(reference.get());}
}class Order{private String id;private String name;public Order(String id, String name) {this.id = id;this.name = name;}@Overridepublic String toString() {return "Order{" +"id='" + id + '\'' +", name='" + name + '\'' +'}';}public String getId() {return id;}public String getName() {return name;}public void setId(String id) {this.id = id;}public void setName(String name) {this.name = name;}}

 运行结果:

总结:

        原子类一般只能使用一个字段,当我们有多个字段的时候,是不能使用一般的原子类的,这是我们可以使用AtomicReference操作多个字段。

14.剖析未可见性问题

package imooc.visibility;/*** @Description: 库存类*/
public class Inventory {//库存数量private   Integer count;//商品名称private String product;//用户下单用public void deCount(){count--;}//管理员补充库存用public void addCount(int num){count+=num;}public Inventory(Integer count, String product) {this.count = count;this.product = product;}public Integer getCount() {return count;}public void setCount(Integer count) {this.count = count;}public String getProduct() {return product;}public void setProduct(String product) {this.product = product;}//判断是否有库存,有库存可以下单public boolean hasInventory(){return count>0;}
}
package imooc.visibility;/*** @Description: 测试下单和补库存*/
public class TestInventory {public static void main(String[] args) throws InterruptedException {//初始化库存Inventory inventory = new Inventory(100,"苹果");//模拟用户下单new Thread(()->{while (true){//疯狂下单if(inventory.hasInventory()){//有库存 扣库存inventory.deCount();System.out.println("开始购物,库存还有 :"+inventory.getCount());}}}).start();//等待用户下单结束Thread.sleep(3000);//管理员加库存 10个inventory.addCount(100);}
}

运行结果:

分析:

        一开始设置库存10,每个线程的缓存10个,当进行补货后,结果还是无法继续下单。

优化:

运行结果:

15.volatile可见性剖析

package imooc.visibility;/*** @Description:*/
public class TestVolatile {public static volatile int i =0;
//public static void testVolatile1(){i = 1;}public static void main(String[] args) {testVolatile1();}
}

查看汇编:

在 Java 中,`volatile` 关键字用于确保多线程环境下变量的可见性和有序性。为了实现这些语义,JVM 和底层硬件(如 CPU)会使用一些机制,其中**总线嗅探(Bus Snooping)**是一个与缓存一致性相关的重要概念。---### 什么是总线嗅探?
总线嗅探是一种**缓存一致性协议**的实现方式,用于在多核 CPU 系统中维护多个核心的缓存一致性。它的工作原理如下:1. **总线的监听**:- 每个 CPU 核心都会监听总线上的内存操作(如读、写)。- 当一个核心修改了某个共享变量的值,这个修改操作会通过总线广播给其他核心。2. **缓存行的失效**:- 如果其他核心的缓存中有该变量的副本,总线嗅探机制会将这些副本标记为**失效**(Invalid)。- 这样,其他核心在读取该变量时,会从主内存中重新加载最新的值,而不是使用本地缓存中的旧值。3. **保证可见性**:- 通过总线嗅探,`volatile` 变量的修改能够立即被其他线程看到,从而实现了**可见性**。---### `volatile` 和总线嗅探的关系
在 Java 中,`volatile` 变量的读写会触发以下操作:1. **写操作**:- 当一个线程修改 `volatile` 变量时,JVM 会确保该修改立即写入主内存,而不是仅仅停留在本地缓存。- 同时,总线嗅探机制会通知其他核心,使它们缓存中的该变量副本失效。2. **读操作**:- 当一个线程读取 `volatile` 变量时,JVM 会强制从主内存中加载最新的值,而不是使用本地缓存中的旧值。通过总线嗅探机制,`volatile` 实现了以下特性:
- **可见性**:一个线程对 `volatile` 变量的修改对其他线程立即可见。
- **有序性**:JVM 会禁止指令重排序,确保 `volatile` 写操作之前的操作不会被重排序到写操作之后,读操作之后的操作不会被重排序到读操作之前。---### 总线嗅探的缺点
虽然总线嗅探是实现缓存一致性的一种有效方式,但它也有一些性能上的缺点:
1. **总线带宽压力**:- 每次 `volatile` 写操作都会触发总线广播,增加了总线的通信压力。- 在高并发场景下,频繁的 `volatile` 写操作可能导致总线带宽成为瓶颈。2. **缓存行失效**:- 总线嗅探会导致其他核心的缓存行失效,增加了缓存未命中(Cache Miss)的概率,从而影响性能。---### 总线嗅探的替代方案
在现代 CPU 中,除了总线嗅探,还有其他缓存一致性协议,例如:
- **MESI 协议**(Modified, Exclusive, Shared, Invalid):- 一种更高效的缓存一致性协议,通过状态机来管理缓存行的状态,减少总线通信的开销。---### 总结
- 总线嗅探是实现 `volatile` 可见性的底层机制之一,它通过监听总线上的内存操作来维护缓存一致性。
- `volatile` 通过总线嗅探和内存屏障(Memory Barrier)来保证可见性和有序性。
- 虽然总线嗅探有效,但在高并发场景下可能会带来性能开销,因此需要谨慎使用 `volatile`。

 框架中的使用:

 

解释:

        一个long类型8byte,RhsPadding中7个long类型、LhsPadding中1个long类型,刚好填充满缓存行64个byte。

16.单例的创建方式及原理深入剖析

代码示例:

package imooc.singleton;/*** @Description: 单利模式的类*/
public class Singleton {public static volatile Singleton singleton ;/*** 私有化构造方法 ,外部不能调用*/private Singleton() {}public static  Singleton getSingleton(){if(singleton==null){synchronized (Singleton.class){//如果singleton是null,初始化if(singleton==null){singleton = new Singleton();//1 开辟空间 0: new//2 初始化字段内容 invokespecial #1//3 指向堆空间 astore_1}}}return singleton;}//test case用public static void reset(){singleton=null;}public static void main(String[] args) {}
}

package imooc.singleton;import com.alibaba.dubbo.common.utils.ConcurrentHashSet;import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;/*** @Description:*/
public class TestSingleton {public static void main(String[] args) throws InterruptedException {while (true){Set<Integer> set = new ConcurrentHashSet<>();List<Thread> list = new ArrayList<>();CountDownLatch countDownLatch = new CountDownLatch(1);for (int i = 0; i < 10; i++) {Thread thread = new Thread(() -> {try {//让所有线程启动后阻塞(准备完毕),一起并发countDownLatch.await();} catch (InterruptedException e) {throw new RuntimeException(e);}Singleton singleton = Singleton.getSingleton();set.add(singleton.hashCode());});list.add(thread);}for (Thread thread : list) {thread.start();}//主线程将技术减为0,让所有线程一起往下执行并发countDownLatch.countDown();for (Thread thread : list) {thread.join();}if(set.size()>1){System.out.println("fail to create singleton");}Singleton.reset();}}
}

期望:

        如果控制台未输出信息,证明为单例。

运行结果: 

注意:

        这里要双重判断singleton==null,如果同时有两个线程同时进入第一个singleton==null判断,其中一个获得了锁,并初始化singleton,然后释放锁,第二个线程获取锁,到第二个singleton==null判断,直接跳出if判断。

(1)在堆中开辟空间;

(2)init对象中的属性;

(3)对象指针指向堆中的内存地址。

(1)在堆中开辟空间;

(3)对象指针指向堆中的内存地址。

(2)init对象中的属性;

结合前面singten的单例初始化代码:

(1)在堆中开辟空间; - 正常

(3)对象指针指向堆中的内存地址。 - 这个时候就产生问题了,如果第一个线程进入到这里,在开辟空间后,先将对象指针指向了堆空间中的内存地址,其他线程此时获取对象,获取到的是一个没有初始化完的对象,所以我们要禁止这种重排序的现象。

17.volatile禁止重排序的原理

package imooc.singleton;import java.util.concurrent.CountDownLatch;/*** @Description: 测试重排序* 创建两个线程,测试线程内重排序现象*/
public class TestReorder {//禁止重排序 加volatilestatic volatile int x = 0;static volatile int y = 0;static volatile int a = 0;static volatile int b = 0;public static void main(String[] args) throws InterruptedException {int count = 0;while (true){CountDownLatch countDownLatch = new CountDownLatch(1);Thread thread = new Thread(() -> {try {countDownLatch.await();} catch (InterruptedException e) {throw new RuntimeException(e);}//操作1a = 1;//操作2x = b;System.out.println("TestLog: "+a+" b: "+b);});Thread thread1 = new Thread(() -> {try {countDownLatch.await();} catch (InterruptedException e) {throw new RuntimeException(e);}//操作1b = 1;//操作2y = a;System.out.println("TestLog: "+a+" b: "+b);});thread.start();thread1.start();countDownLatch.countDown();thread.join();thread1.join();//三种情况 (1,1)(1,0),(0,1)//如果两个线程都发生了重排序,结果是(0,0)System.out.println("x: "+ x+",y: "+y);//如果重排序,退出if(x==0&&y==0){System.out.println("x=0&y=0,count="+count);break;}count++;x=0;y=0;a=0;b=0;System.out.println("=============");}}
}

package imooc.singleton;/*** @Description:编译器级别重排序*/
public class TestReorder1 {static volatile int a = 0;static int b = 0;public static void main(String[] args) {//第一个操作 普通变量的读写b = 10;//第二个操作,volatile变量的写a = 10;}
}

编译器转为字节码,最后转换为汇编代码的时候,还会有一个处理器级别的重排序 

解释:

  1. LoadLoad(读读屏障):先执行屏障前的 ,后执行屏障后的 
  2. LoadStore(读写屏障):先执行屏障前的 ,后执行屏障后的 
  3. StoreLoad(写读屏障):先执行屏障前的 ,后执行屏障后的 
  4. StoreStore(写写屏障):先执行屏障前的 ,后执行屏障后的 

参考:Java四种内存屏障详解,LoadLoad、LoadStore、StoreLoad、StoreStore - Yfeil - 博客园


18.volatile在dubbo和秒杀中的应用

​​​​​​​

package imooc.test;
/*** @Author: Alfred* @ModuleOwner: Alfred* @Description: 修改温度的范围*/
//线程不安全
public class TemperatureChanger {//兔子能适应的最低温度private volatile int lower = 16;//兔子能适应的最高温度private volatile int upper = 25;//限制value不能高于upper 16 -25public void setLower(int value){if(value>upper){throw new RuntimeException("温度不能高于上限");}this.lower = value;}//限制value不能低于lowerpublic void setUpper(int value){if(value<lower){throw new RuntimeException("温度不能低于下限");}this.upper = value;}public int getLower() {return lower;}public int getUpper() {return upper;}public static void main(String[] args) {TemperatureChanger temperatureChanger = new TemperatureChanger();new Thread(()->{temperatureChanger.setLower(23);}).start();new Thread(()->{temperatureChanger.setUpper(20);}).start();//23-20}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/18552.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IntelliJ IDEA 接入 AI 编程助手(Copilot、DeepSeek、GPT-4o Mini)

IntelliJ IDEA 接入 AI 编程助手&#xff08;Copilot、DeepSeek、GPT-4o Mini&#xff09; &#x1f4ca; 引言 近年来&#xff0c;AI 编程助手已成为开发者的高效工具&#xff0c;它们可以加速代码编写、优化代码结构&#xff0c;并提供智能提示。本文介绍如何在 IntelliJ I…

积家(Jaeger-LeCoultre):“钟表界的钟表师“(中英双语)

积家&#xff08;Jaeger-LeCoultre&#xff09;&#xff1a;瑞士高级制表的隐形巨匠 在瑞士高级制表领域&#xff0c;积家&#xff08;Jaeger-LeCoultre&#xff0c;简称JLC&#xff09; 被誉为“钟表界的钟表师”&#xff0c;它不仅是世界顶级腕表品牌之一&#xff0c;还为许…

Jenkins 新建配置Pipeline任务 三

Jenkins 新建配置Pipeline任务 三 一. 登录 Jenkins 网页输入 http://localhost:8080 输入账号、密码登录 一个没有创建任务的空 Jenkins 二. 创建 任务 图 NewItem 界面左上角 New Item 图NewItemSelect 1.Enter an item name&#xff1a;输入任务名 2.Select an ite…

盛铂科技 SMF106 低相位噪声贴片式频率综合器模块

在现代通信和电子设备领域&#xff0c;频率综合器作为关键组件&#xff0c;其性能优劣直接影响系统的整体表现。盛铂科技的 SMF106 低相位噪声贴片式频率综合器&#xff0c;以其卓越的性能和独特设计&#xff0c;成为众多高性能系统的选择。 一、频率覆盖范围广&#xff0c;步进…

ros:ur机械臂初识

这是用来可视化的launch文件 比如&#xff0c;我运行 roslaunch ur_description view_ur3.launch ur3模型 ur3e模型 ur5模型 ur5e模型 ur10模型 ur20模型 ur30模型 后来我搜了一下 UR5 和 UR10 都是由 Universal Robots&#xff08;简称 UR&#xff09;生产的协作机器人&…

智能陪诊与远程问诊:AI驱动的互联网医院APP开发路线图

智能陪诊与远程问诊作为现在医疗变革的前沿阵地&#xff0c;正在为广大患者提供更为便捷、高效的医疗服务。特别是在互联网医院APP的开发过程中&#xff0c;AI技术的应用已成为提升用户体验和医疗服务质量的重要手段。本文将探讨如何基于AI技术开发智能陪诊与远程问诊功能的互联…

pnpm, eslint, vue-router4, element-plus, pinia

利用 pnpm 创建 vue3 项目 pnpm 包管理器 - 创建项目 Eslint 配置代码风格(Eslint用于规范纠错&#xff0c;prettier用于美观&#xff09; 在 设置 中配置保存时自动修复 提交前做代码检查 husky是一个 git hooks工具&#xff08;git的钩子工具&#xff0c;可以在特定实际执行特…

格式工厂 FormatFactory v5.18.便携版 ——多功能媒体文件转换工具

格式工厂 FormatFactory v5.18.便携版 ——多功能媒体文件转换工具 功能&#xff1a;视频 音频 图片 文档PDF格式 各种转换&#xff0c;同格式调整压缩比例&#xff0c;调整大小 特色&#xff1a;果风图标 好看; 支持多任务队列&#xff0c;完成自动关机 下载地址&#xff1…

ai数字人分身系统开发源码saas化

#数字人分身系统# #数字人系统源码# #ai数字人123 123# 云罗抖去推数字人分身系统是一款融合了形象克隆、声音克隆、AI数字人分身、AI智能剪辑、智能文案等各种AI技术一体化的短视频营销工具&#xff0c;其核心功能优势主要体现在以下几方面&#xff1a; 真实度高&#xf…

基于Spring Boot的民宿租赁系统的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

李宏毅机器学习笔记:【6.Optimization、Adaptive Learning Rate】

Optimization 1.Adaptive Learning Rate2.不同的参数需要不同的学习率3.Root Mean Square4.RMSProp5.Adam6.learning rate scheduling7.warm up总结 critical point不一定是你在训练一个network时候遇到的最大的障碍。 1.Adaptive Learning Rate 也就是我们要给每个参数不同的…

CAS单点登录(第7版)2.规划

如有疑问&#xff0c;请看视频&#xff1a;CAS单点登录&#xff08;第7版&#xff09; 规划 架构 系统组件 CAS 服务器和客户端构成了 CAS 系统体系结构的两个物理组件&#xff0c;它们通过各种协议进行通信。 CAS 服务器 CAS 服务器是基于 Spring Framework 构建的 Ja…

wx061基于ssm+vue+uniapp的疫情期间学生请假与销假系统小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

【动态规划】详解 0-1背包问题

文章目录 1. 问题引入2. 从 dfs 到动态规划3. 动态规划过程分析4. 二维 dp 的遍历顺序5. 从二维数组到一维数组6. 一维数组的遍历次序7. 背包的遍历顺序8. 代码总结9. 总结 1. 问题引入 0-1 背包是比较经典的动态规划问题&#xff0c;这里以代码随想录里面的例子来介绍下。总的…

【设计模式】【行为型模式】解释器模式(Interpreter)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f3b5; 当你的天空突…

CAS单点登录(第7版)1.首页

如有疑问&#xff0c;请看视频&#xff1a;CAS单点登录&#xff08;第7版&#xff09; 面向所有地球人及其他地区的企业身份 Enterprise Identity for All Earthlings and Beyond 身份、单点登录和访问管理 Identity, Single Sign-On and Access Management 首页 Apereo CAS…

数据库数据恢复—MongoDB丢失_mdb_catalog.wt文件导致报错的数据恢复案例

MongoDB数据库存储模式为文档数据存储库&#xff0c;存储方式是将文档存储在集合之中。 MongoDB数据库是开源数据库&#xff0c;同时提供具有附加功能的商业版本。 MongoDB中的数据是以键值对(key-value pairs)的形式显示的。在模式设计上&#xff0c;数据库受到的约束更少。这…

SpringCloud中Sentinel基础场景和异常处理

Sentinel 是一个由 阿里巴巴 开源的分布式系统流量控制组件&#xff0c;专注于为微服务架构提供流量控制、熔断降级、系统负载保护等功能。它特别适用于高并发、高可用性的分布式系统&#xff0c;能够帮助开发者保护系统免于因流量过载、系统崩溃、依赖不可用等情况而导致的服务…

探索C语言中判断字符串循环移位关系的实现

在C语言的字符串处理中&#xff0c;判断两个字符串是否为循环移位关系是一个有趣且实用的问题。今天&#xff0c;我们就通过一段具体的代码来深入探讨这个问题的解决方案。 代码实现 代码逐行解析 预处理指令和头文件包含 #define _CRT_SECURE_NO_WARNINGS 用于禁用一些与安全…

Uniapp 原生组件层级过高问题及解决方案

文章目录 一、引言&#x1f3c5;二、问题描述&#x1f4cc;三、问题原因❓四、解决方案&#x1f4af;4.1 使用 cover-view 和 cover-image4.2 使用 subNVue 子窗体4.3 动态隐藏原生组件4.4 使用 v-if 或 v-show 控制组件显示4.5 使用 position: fixed 布局 五、总结&#x1f38…