public static void main(String[] args) { //开启线程 Thread t1 = new Thread(() -> { //todo }); t1.start(); 复制代码
如上一个简单的线程他从启动到调用run方法是怎么一个流程,往下看线程 start
方法的源码发现核心的方法应该就是调用 private native void start0();
这个方法是 native
用c写的。这个方法底层在调用jvm,jvm直接调用操作系统 pthread_create
方法启动操作系统的一个线程,同时jvm维护一个线程对象跟操作系统的线程一一对应,同时和java的 Thread
一一对应,调用java的线程对象相关api相当于操作操作系统的线程,在操作系统的 pthread_create
的方法中执行的时候再来回调java的 run
方法。
java线程调用过程如下
1.idae环境 2.需要依赖jol(可以打印出对象对象头)
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.9</version> </dependency> 复制代码
synchronized
是什么 synchronized
是 java
的关键字,是 jvm
底层实现的,1.6前只有重量级锁,1.6 jvm
对其进行优化,增加了 偏向锁
和 轻量级锁
要了解 synchronized
锁膨胀过程需要了解对象的对象头 //todo 差一个对象头图片
顾名思义,偏向,偏心,偏向线程,当只有一个线程访问同步代码的时候,即是偏向锁,每次要执行同步代码的时候,都会先判断线程id是否一致,如果一致就直接进入同步块
1.主线程不睡眠的情况
public static void main(String[] args) throws InterruptedException { User user = new User(); //jvm延迟偏向锁,所以这时候无锁锁标志 01 System.out.println("first print" + ClassLayout.parseInstance(user).toPrintable()); //此时偏向锁关闭,进入同步代码块,变为轻量级锁 00 synchronized (user) { System.out.println("first into sync" + ClassLayout.parseInstance(user).toPrintable()); } } 复制代码
下面是根据jol打印出来的对象头 mark work
第一次打印: 00000001 00000000 00000000 00000000
锁标志位是01 无锁
00001000 11110111 10010011 00000010
锁标志位00 轻量级锁
2.主线程睡眠的情况
public static void main(String[] args) throws InterruptedException { //睡眠大概五秒之后,jvm偏向锁已经开启 Thread.sleep(5000L); User user = new User(); //偏向锁,还没有记录线程信息,表示可偏向 101 System.out.println("first print" + ClassLayout.parseInstance(user).toPrintable()); //进入同步代码块,先检查是否可偏向 //如果可偏向,记录线程信息 101 同时记录线程id synchronized (user) { System.out.println("first into sync" + ClassLayout.parseInstance(user).toPrintable()); } } 复制代码
下面是根据jol打印出来的对象头 mark work
第一次打印: 00000101 00000000 00000000 00000000
锁标志101 没有线程id,表示可偏向
00000101 00101000 11110010 00000010
锁标志101 有线程id,表示已经偏向了
同样的代码有睡眠跟没有睡眠锁的标志位不一样,睡眠五秒之后jvm已经开启了偏向锁,所以有睡眠的进入同步代码块就是偏向锁,没有睡眠的,jvm延迟了偏向锁,所以进入同步代码块是轻量级锁。
偏向锁是一直由同一个线程持有锁,但是当发生多个 线程交替 执行同步块的时候就会进行锁升级。
public static void main(String[] args) throws InterruptedException { //睡眠五秒,五秒之后jvm开启轻量级锁 Thread.sleep(5000L); User user = new User(); //偏向锁,还没有记录线程信息,表示可偏向 101 无线程id System.out.println("first print" + ClassLayout.parseInstance(user).toPrintable()); //进入同步代码块,先检查是否可偏向 //如果可偏向,记录线程信息 101 有线程id synchronized (user) { System.out.println("first into sync" + ClassLayout.parseInstance(user).toPrintable()); } //开启线程 此时不同的线程来了,不存在锁竞争,锁升级为轻量级锁 Thread t1 = new Thread(() -> { synchronized (user) { System.out.println("sencond into sync" + ClassLayout.parseInstance(user).toPrintable()); } }); t1.start(); } 复制代码
下面是根据jol打印出来的对象头 mark work
重量级锁是在多线程中,存在 锁竞争 的情况下会出现
public static void main(String[] args) throws InterruptedException { Thread.sleep(5000L); User user = new User(); //偏向锁,但是没有偏向的线程 101 System.out.println("first pring" + ClassLayout.parseInstance(user).toPrintable()); //主线程拿到锁 synchronized (user) { //偏向锁,此时有偏向的地址 101 System.out.println("first in" + ClassLayout.parseInstance(user).toPrintable()); } //线程t2拿到锁 Thread t2 = new Thread(() -> { synchronized (user) { //虽然主线程已经执行完了,但是算是交替执行 //此时是轻量级锁 00 System.out.println("t2 " + ClassLayout.parseInstance(user).toPrintable()); try { //进入锁,先不退出,等待t3来竞争锁 Thread.sleep(7000L); } catch (InterruptedException e) { e.printStackTrace(); } } }); t2.start(); //等待是为了确保t2已经拿到锁了 Thread.sleep(4000L); //线程t3竞争锁 Thread t3 = new Thread(() -> { synchronized (user) { //t2还没执行完就去竞争锁 //存在锁的竞争,锁升级为重量级锁 10 System.out.println("t3 " + ClassLayout.parseInstance(user).toPrintable()); } }); t3.start(); } 复制代码
下面是根据jol打印出来的对象头 mark work