Java内存模型及锁相关知识的简单理解与总结

目录

1.计算机相关知识

2.Java内存模型

3.volatile关键字的理解

4.三大特性

5.线程

6.锁


1.计算机相关知识

1).处理器和内存之间的速度运算速度问题相差悬殊,就为处理器各自配备高速缓存区,但又引出了缓存一致性问题,为解决这个问题,出现了一堆协议,这类协议有MSI、MESI(Illinois Protocol)、MOSI、 Synapse、Firefly及Dragon Protocol等。

JVM也有类似的内存模型;

2).为充分利用处理器内部的计算单元,处理器可能对代码进行乱序执行优化,JVM中即时编译器的指令重排优化与此类似;

2.Java内存模型

  • Java内存模型主要关注的是虚拟机中变量(实例、静态字段及数组中的元素;而非局部变量和方法参数,这两者归线程私有)在内存中存取的底层细节;
  • 具体操作步骤如下:

lock(锁定):作用于主内存的变量,它把一个变量标识为一条线程独占的状态。

read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。

load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的 变量副本中。

use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。

assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收的值赋给工作内存的变量, 每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。

store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。

write(写入):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的 变量中。

unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。

3.volatile关键字的理解

(1)两大特性

     1.保证变量对所有线程的可见性;

       示例如下:

        1).未使用volatile

package com.demo2.test;
public class Job {
    boolean shutdownRequested;
    public void shutdown() {
        shutdownRequested = true;
    }
    public void doWork() {
        int index =0;
        while (!shutdownRequested) {
            index++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Job migrationJob = new Job();
        new Thread(new Runnable() {
            @Override
            public void run() {
                migrationJob.doWork();
            }
        }).start();
        Thread.sleep(200);
        migrationJob.shutdown();

        while (Thread.activeCount() > 2)
            Thread.yield();
        System.out.println("---------------main----------------");
    }
}

使用jconsole查看

main线程:

Thread-0线程:

在代码第3行加入volatile关键字;

package com.demo2.test;
public class Job {
    volatile boolean shutdownRequested;
    public void shutdown() {
        shutdownRequested = true;
    }
    public void doWork() {
        int index =0;
        while (!shutdownRequested) {
            index++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Job migrationJob = new Job();
        new Thread(new Runnable() {
            @Override
            public void run() {
                migrationJob.doWork();
            }
        }).start();
        Thread.sleep(200);
        migrationJob.shutdown();

        while (Thread.activeCount() > 2)
            Thread.yield();
        System.out.println("---------------main----------------");
    }
}

运行结果如下, 程序正常退出;

     2.防止指令重排

注:使用volatile不用考虑原子性的两种场景:

1.运算结果不依赖变量原值或只有单一线程使用该变量(即只有单个线程写)

2.变量不与其它状态变量共同参与不变约束;

4.三大特性

1.原子性

       1.1 对于基本数据类型(double和long的非原子协定不用考虑),通过 read、load、use、assign、store、write六个操作就能实现

               1.2 保证大范围原子性,虚拟机通过字节码指令monitorenter、monitorexit隐式实现Java内存模型中的lock和unlock效果;

        2.可见性

               2.1 其它线程能够立即得知该线程对共享变量的修改(也就是说线程使用共享变量就从主内存中读取,用完立即写回到主内存中);

               2.2 可实现可见性的关键字还有final、synchronized;

        3.有序性

             保证 线程内部所有操作都是有序的

5.线程

  1. 实现线程的三种方式:
    1. 内核线程(1:1)
    2. 用户线程(1:N)
    3. 用户线程加轻量级进程实现(N:M)
  2. Java线程模型

             JDK1.3以后,使用的是1:1的线程模型,全权由系统进行管理和调度;这里只能设置线程优先级(10个级别),来提高被调度的可能性;

      3.线程状态

            Java中有6种线程状态,分别为新建、运行(Runnable对应操作系统中的Running和Ready)、无限期等待(waiting需要其他线程显示唤醒)、限期等待(timed waiting到时会被系统唤醒)、阻

塞(Blocked等待获取排他锁)、结束;

6.锁

  1. 自旋锁:

 JDK1.4.2引入,JDK6后默认开启,

 好处:避免了线程切换的开销;

 弊端:占用了处理器的时间;

 自旋次数:默认10次,可通过-XX:PreBlockSpin修改;

 JDK6中对自旋锁的优化,即自适应自旋锁(成功获得过锁且运行中就认为还有成功的可能,下次也会多等,反之,就不等,免得浪费处理器时间)

      2.锁消除

         虚拟机即时编译器在运行时,对不存在共享数据竞争的同步代码进行锁消除;

         判断依据:逃逸分析(即一段代码在堆中的所有数据不存在被其他线程访问到的可能,就当作线程私有数据对待);

         例子:JDK5之后,字符串加法由StringBuffer.append() 转变为StringBuilder.append()实现

      3.锁粗化

         避免因零碎的操作对同一对象不段加锁,放大锁定的范围;

       4.轻量级锁

           前提知识:了解虚拟机中的对象头

           对象头分为两部分:

                 1.用于存储对象自身的运行时数据(如哈希码、GC分代年龄等)即Mark word;是实现轻量级锁和偏向锁的关键;在32位和64位虚拟机分别对应32和64比特;

                 2.用于存储指向方法区对象类型数据的指针(数组还存储数组长度);

          加锁过程:对象在无锁状态下,虚拟机会在当前线程的栈帧中建立一块名为锁记录(Lock Record)的Mark word 拷贝(Displaced Mark Word),然后虚拟机将尝试使用CAS来将对象的mark word 更换为指向 锁记录(Lock Record)的指针,并将锁标志位改为“00”;如果失败,则判断对象的mark word 是否指向该线程的栈帧,若是,则直接执行同步代码,否则轻量级锁将膨胀为重量级锁,mark word 变为指向重量级锁(互斥量)的指针,标志位变为“01”;

          解锁过程:使用CAS将mark word和Mark word 拷贝(Displaced Mark Word)互换,若替换失败,则换的同时要唤醒被挂起的线程;

          

     5.偏向锁

         JDK6引入

         目的:消除数据在无竞争下的同步原语(synchronized、volatile),进一步提高运行性能

          

参考资料:《深入理解Java虚拟机》

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值