JUC并发编程系列详解篇九(synchronized基础)

java的对象头

在JVM中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。如下图所示:
JUC并发编程系列详解篇九(synchronized基础)_第1张图片

对象头
对象头又包括两部分信息,第一部分用于存储对象自身的运行时数据(Mark Word),如HashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。对象头的另外一部分是类型指针(Klass pointer),即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
JUC并发编程系列详解篇九(synchronized基础)_第2张图片

值得注意的是:类元信息存在于方法区,类元信息有区别与堆中的synchClass字节码对象,synchClass可以理解为类加载完成后,JVM将类的信息存在堆中,然后使用反射去访问其全部信息(包括函数和字段),然而在JVM内部大多数对象都是使用C++代码实现的,对于JVM内部如果需要类信息,JVM就会通过对象头的类型指针去拿方法区中类元信息的数据。

实例数据:存放类的属性数据信息,包括父类的属性信息。

对齐填充:由于虚拟机要求 对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。

synchronized作用的锁

synchronized用的锁是存在Java对象头里的。如果对象是数组类型,则虚拟机用3个字宽(Word)存储对象头,如果对象是非数组类型,则用2字宽存储对象头。在32位虚拟机中,1字宽等于4字节,即32bit。如下图所示:
在这里插入图片描述
Java对象头里的Mark Word里默认存储对象的HashCode、分代年龄和锁标记位。32位JVM的Mark Word的默认存储结构如下图所示:
在这里插入图片描述
在运行期间,Mark Word里存储的数据会随着锁标志位的变化而变化。Mark Word可能变化为存储以下4种数据,如下图所示:
JUC并发编程系列详解篇九(synchronized基础)_第3张图片
在64位虚拟机下,Mark Word是64bit大小的,其存储结构如下图所示:
JUC并发编程系列详解篇九(synchronized基础)_第4张图片

指针压缩

现在虚拟机基本是64位的,而64位的对象头有点浪费空间,JVM默认会开启指针压缩,所以基本上也是按32位的形式记录对象头的。也可以通过下面参数进行控制JVM开启和关闭指针压缩:

  • 开启压缩指针(-XX:+UseCompressedOops)
  • 关闭压缩指针(-XX:-UseCompressedOops)

为什么JVM需要默认开启指针压缩呢?

原因在于在对象头上类元信息指针Klass pointer在32位JVM虚拟机中用4个字节存储,但是到了64位JVM虚拟机中Klass pointer用的就是8个字节来存储,一些对象在32位虚拟机用的也是4字节来存储,到了64位机器用的都是8字节来存储了,一个工程项目中有成千上万的对象,倘若每个对象都用8字节来存放的话,那这些对象无形中就会增加很多空间,导致堆的压力就会很大,堆很容易就会满了,然后就会更容易的触发GC,那指针压缩的最主要的作用就是压缩每个对象内存地址的大小,那么同样堆内存大小就可以放更多的对象。

为什么JVM中的对象由年轻代进入老年代的默认分代年龄是15?
对象头中有4个字节用于存放对象分代年龄的,4个字节就是2的四次方等于16,其范围就是0~15,所以也就很好理解对象在GC的时候,JVM对象由年轻代进入老年代的默认分代年龄是15了。

synchronized的基础用法

synchronized 通过当前线程持有对象锁,从而拥有访问权限,而其他没有持有当前对象锁的线程无法拥有访问权限,保证在同一时刻,只有一个线程可以执行某个方法或者某个代码块,从而保证线程安全。synchronized 可以保证线程的可见性,synchronized 属于隐式锁,锁的持有与释放都是隐式的,我们无需干预。synchronized最主要的三种应用方式:

  • 修饰实例(类)方法:作用于当前实例加锁,进入同步代码前要获得当前实例的锁。如果多个线程不同对象访问该方法,则无法保证同步。

  • 修饰静态方法:作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁。锁的是包含这个方法的类,也就是类对象,这样如果多个线程不同对象访问该静态方法,也是可以保证同步的。

  • 修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

在应用Sychronized关键字时需要把握如下注意点:

  • 一把锁只能同时被一个线程获取,没有获得锁的线程只能等待;
  • 每个实例都对应有自己的一把锁(this),不同实例之间互不影响;例外:锁对象是*.class以及synchronized修饰的是static方法的时候,所有对象公用同一把锁
  • synchronized修饰的方法,无论方法正常执行完毕还是抛出异常,都会释放锁

synchronized修饰代码块

方法锁(默认锁对象为this,当前实例对象)和同步代码块锁(自己指定锁对象)。

手动指定锁定对象,也可是是this,也可以是自定义的锁

public class SynchronizedObjectLock implements Runnable{
    static SynchronizedObjectLock instence = new SynchronizedObjectLock();
    @Override
    public void run() {
        // 同步代码块形式——锁为this,两个线程使用的锁是一样的,线程1必须要等到线程0释放了该锁后,才能执行
        synchronized (this) {
            System.out.println("我是线程" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "结束");
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instence);
        Thread t2 = new Thread(instence);
        t1.start();
        t2.start();
    }
}

结果:
JUC并发编程系列详解篇九(synchronized基础)_第5张图片
实例代码2:

public class SynchronizedObjectLock implements Runnable{
    static SynchronizedObjectLock instence = new SynchronizedObjectLock();
    // 创建2把锁
    Object block1 = new Object();
    Object block2 = new Object();

    @Override
    public void run() {
        // 这个代码块使用的是第一把锁,当他释放后,后面的代码块由于使用的是第二把锁,因此可以马上执行
        synchronized (block1) {
            System.out.println("block1锁,我是线程" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("block1锁,"+Thread.currentThread().getName() + "结束");
        }

        synchronized (block2) {
            System.out.println("block2锁,我是线程" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("block2锁,"+Thread.currentThread().getName() + "结束");
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instence);
        Thread t2 = new Thread(instence);
        t1.start();
        t2.start();
    }
}

结果:
JUC并发编程系列详解篇九(synchronized基础)_第6张图片
synchronized修饰普通方法,锁对象默认为this

public class SynchronizedObjectLock implements Runnable {
    static SynchronizedObjectLock instence = new SynchronizedObjectLock();

    @Override
    public void run() {
        method();
    }

    public synchronized void method() {
        System.out.println("我是线程" + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "结束");
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instence);
        Thread t2 = new Thread(instence);
        t1.start();
        t2.start();
    }
}

结果:
JUC并发编程系列详解篇九(synchronized基础)_第7张图片

synchronized修饰静态方法

实例代码:

public class SynchronizedObjectLock implements Runnable {
    static SynchronizedObjectLock instence1 = new SynchronizedObjectLock();
    static SynchronizedObjectLock instence2 = new SynchronizedObjectLock();

    @Override
    public void run() {
        method();
    }

    // synchronized用在普通方法上,默认的锁就是this,当前实例
    public synchronized void method() {
        System.out.println("我是线程" + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "结束");
    }

    public static void main(String[] args) {
        // t1和t2对应的this是两个不同的实例,所以代码不会串行
        Thread t1 = new Thread(instence1);
        Thread t2 = new Thread(instence2);
        t1.start();
        t2.start();
    }
}

结果:
JUC并发编程系列详解篇九(synchronized基础)_第8张图片
实例代码2:

public class SynchronizedObjectLock implements Runnable {
    static SynchronizedObjectLock instence1 = new SynchronizedObjectLock();
    static SynchronizedObjectLock instence2 = new SynchronizedObjectLock();

    @Override
    public void run() {
        method();
    }

    // synchronized用在静态方法上,默认的锁就是当前所在的Class类,所以无论是哪个线程访问它,需要的锁都只有一把
    public static synchronized void method() {
        System.out.println("我是线程" + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "结束");
    }

    public static void main(String[] args) {
        // t1和t2对应的this是两个不同的实例,所以代码不会串行
        Thread t1 = new Thread(instence1);
        Thread t2 = new Thread(instence2);
        t1.start();
        t2.start();
    }
}

结果:
JUC并发编程系列详解篇九(synchronized基础)_第9张图片

synchronized修饰实例

实例代码:

public class SynchronizedObjectLock implements Runnable {
    static SynchronizedObjectLock instence1 = new SynchronizedObjectLock();
    static SynchronizedObjectLock instence2 = new SynchronizedObjectLock();

    @Override
    public void run() {
        // 所有线程需要的锁都是同一把
        synchronized(SynchronizedObjectLock.class){
            System.out.println("我是线程" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "结束");
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(instence1);
        Thread t2 = new Thread(instence2);
        t1.start();
        t2.start();
    }
}

结果:
JUC并发编程系列详解篇九(synchronized基础)_第10张图片

参考文章

  • https://www.pdai.tech/md/java/thread/java-thread-x-key-synchronized.html
  • 微信公众号(得物技术) :精选文章|深入理解synchronzied底层原理
  • https://blog.csdn.net/a745233700/article/details/119923661
  • 《深入理解Java虚拟机》+《Java并发编程的艺术》
  • https://juejin.im/post/5ae6dc04f265da0ba351d3ff
  • https://www.cnblogs.com/javaminer/p/3889023.html
  • https://www.jianshu.com/p/dab7745c0954
  • https://www.cnblogs.com/wuchaodzxx/p/6867546.html
  • https://www.cnblogs.com/xyabk/p/10901291.html
  • https://www.jianshu.com/p/64240319ed60

你可能感兴趣的:(java基础,Java高级特性,并发编程,jvm,java)