参考https://blog.csdn.net/javazejian/article/details/72772461

Java内存划分:分为方法区,堆,虚拟机栈,本地方法栈,程序计数器

Java内存模型

JVM运行程序的实体是线程,每个线程创建时JVM都会为其创建一个工作内存(有些地方也叫栈空间),用于存储线程私有的数据.而Java内存模型中规定所有变量都存储在主内存,主内存是共享的内存区域,所有线程都可以访问.但是线程对变量的操作必须在工作内存中进行,所以一般是将主内存拷贝到工作内存中去,操作完之后再写回主内存.不会直接操作主内存的变量.不同的线程无法访问对方的工作内存,线程间的通信是通过主内存来完成的.

目的:

为了程序执行的原子性,有序性,可见性.

  1. 原子性是指一个操作不可中断,即使在多线程环境下,一个操作一旦开始就不会被其他线程影响.对于32位的虚拟机来说,每次原子读写是32位,而long和double是64位的存储单元,每次都是一半一半的读写,这样就不具备原子性.

    指令重排:

    a. 编译器优化的重排

    b. 指令并行的重排

    c. 内存系统的重排

  2. 可见性是指当一个线程修改了某个共享变量的值,其他线程是否能够马上得知这个修改的值

  3. 有序性是指多线程环境下会出现指令重排现象

实例对象是存于堆中,其中的成员变量不管是基本数据类型还是包装类型还是引用类型都是存于堆中.

而方法中的本地变量是存于帧栈中的

happens-before原则

volatile内存语义:

JVM提供的轻量级同步机制.

作用1:保证被volatile修饰的共享变量对所有线程总是可见的(通过写回主内存)

作用2:禁止指令重排序优化.(内存屏障)

例子:

public class DoubleCheckLock{

private volatile static DoubleCheckLock instance;

private DoubleCheckLock(){}

public static DoubleCheckoutLock getInstance(){

    if(instance == null){

        synchronized(DoubleCheckoutLock.class){

            if(instance == null){

                instance = new DoubleCheckoutLock();

            }

        }

    }

}

}

参考https://blog.csdn.net/javazejian/article/details/72828483

synchronized的三种应用方式

修饰实例方法:

对当前实例加锁,进入同步代码前需要获得当前实例的锁.

锁住了当前实例,不能访问当前实例其他synchronized方法,可以访问其他非synchronized方法.

若是两个实例的synchronized方法,则是可以同时访问的,因为是两个实例同步锁并不相同,此时若两个线程操作共享数据,则会引起线程不安全.

修饰静态方法

对当前类加锁,与当前实例对象锁不互斥.

修饰代码块

synchronized(instance){}当前实例的代码块

synchronized(class){}当前类的代码块

synchronized底层原理:(基于mutex锁)

在JVM中,对象在内存中布局分为三块区域:对象头,实例变量,填充数据

对象头由两部分组成:

MarkWord:

ClassMetadataAddress:

锁的状态有4种:无锁状态,偏向锁,轻量级锁,重量级锁

锁的升级是单向的

偏向锁:在实际应用中,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得.因此为了减少同一线程获取锁的代价(此出涉及到CAS操作)而引入偏向锁.如果一个线程获得了锁,当这个线程再次请求锁时,无需再获取锁,对于多线程竞争的场景,偏向锁会失效,升级为轻量级锁.

轻量级锁:适合线程交替执行同步块的场合

自旋锁:考虑到线程持有锁的时间不会太长,如果直接挂起操作系统层面的线程可能会得不偿失,因为线程之间的切换会很耗时.因此自旋锁假设不久将来,当前线程可以获得锁,会让线程做几个空循环,一般50个到100个.若经过若干次循环后还是不能获得锁,线程就只能挂起.

锁消除:Java虚拟机在JIT编译时,会去除不可能存在共享资源的锁,可以节省请求锁的时间.

可重入性:一个线程得到一个对象锁之后再次请求该对象锁是允许的.

中断对正在等待获取所对象的synchronized方法不起作用.

等待唤醒机制主要有notify,notifyAll和wait()方法,在使用这3个方法时必须处于synchronized代码块中或者方法中.否则会抛出IllegalMonitorStateException异常.

与sleep方法不同的是wait方法调用完成后,线程将被暂停,但wait方法会释放当前持有的监视器锁(monitor),直到有线程notify/notifyAll方法后才能继续执行.而sleep方法只让线程休眠并不释放锁.同时notify/notifyAll方法调用后,并不会马上释放监视器锁,而在相应的synchronized(){}/synchronized方法执行结束后才自动释放锁.