文章目录


用户态与内核态

java中锁的概念_java
linux操作系统体系架构分为用户态和内核态,内核控制计算机的硬件资源,并提供上层应用程序的运行1环境,用户态即上层应用程序的活动空间,应用程序必须依托于内核提供的资源,对于轻量级别的锁,经过用户态,不经过内核态,重量级别的锁在一定程度上会经过内核态

CAS 原理

CAS : Compare and Swap 即比较再交换
CAS是一种无锁的算法,有3个操作数,内存值V,旧的预期值A,要修改的新值B,当且只当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做

    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[100];
        CountDownLatch latch = new CountDownLatch(threads.length);

        for (int i = 0; i < threads.length; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    a.incrementAndGet();
                }
                latch.countDown();
            });
        }
        Arrays.stream(threads).forEach((t) -> t.start());
        latch.await();
        System.out.println(a);
    }

其中a.incrementAndGet() 深入其中的源码

  public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

调用了 getAndAddInt

 public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

compareAndSwapInt

  public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

对于java中的native关键字,凡是一种语言,都希望是纯。比如解决某一个方案都喜欢就单单这个语言来写即可。Java平台有个用户和本地C代码进行互操作的API,称为Java Native Interface (Java本地接口)。

对于一个新创建的对象,整个对象在内存中的布局

引入依赖

     <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.9</version>
        </dependency>

        Object o = new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());

结果在这里插入代码片

java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     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)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)Instance size: 16 bytesSpace losses: 0 bytes internal + 4 bytes external = 4 bytes total

对于一个新创建的类,对64位的机器前8位为markword,后4位为class point 指定对象引用的类,由于没有成员变量,因此只有12位,故而往后补4位(loss due to the next object alignment),方便被8整除

将对象上锁后的变化

synchronized (o){
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }

java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           38 f7 66 03 (00111000 11110111 01100110 00000011) (57079608)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)Instance size: 16 bytesSpace losses: 0 bytes internal + 4 bytes external = 4 bytes total

锁的变化

因此可以观察到 markword的变化,故而makeword记录了对象的锁信息(还有jvm信息)
偏向锁:偏向锁假定将来只有第一个申请锁的线程会使用锁(不会有任何线程再来申请锁)
无锁态(new 新创建的对象)–>偏向锁–>轻量级锁(自旋锁)–>重量级锁
通过MarkWord中的8个字节也就是64位来记录锁信息。
锁升级过程详解:
当给一个对象增加synchronized锁之后,相当于上了一个偏向锁。
当有一个线程去请求时,就把这个对象MarkWord的ID改为当前线程指针ID(JavaThread),只允许这一个线程去请求对象。
当有其他线程也去请求时,就把锁升级为轻量级锁。每个线程在自己的线程栈中生成LockRecord,用CAS自旋操作将请求对象MarkWordID改为自己的LockRecord,成功的线程请求到了该对象,未成功的对象继续自旋。
如果竞争加剧,当有线程自旋超过一定次数时(在JDK1.6之后,这个自旋次数由JVM自己控制),就将轻量级锁升级为重量级锁,线程挂起,进入等待队列,等待操作系统的调度。