java内存模型

java内存模型(Java Memory Model)规范:

         JMM规定了所有的变量都存储在主内存(Main Memory)中,这里所说的变量指的是实例变量和类变量。不包含局部变量,局部变量的线程私有的,不存在竞争问题。

        每个线程还有自己的工作内存(Working Memory),线程的工作内存中保存了该线程使用到的变量的主内存的副本拷贝。

        线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量(volatile变量仍然有工作内存的拷贝,但是由于它特殊的操作顺序性规定,所以看起来如同直接在主内存中读写访问一般)。

        不同的线程之间也无法直接访问对方工作内存中的变量,线程之间值的传递都需要通过主内存来完成。

        在Java中,所有实例对象、静态变量和数组元素都存储在堆内存中,堆内存在线程之间共享,局部变量,方法定义参数和异常处理器参数不会在线程之间共享,它们不会有内存可见性问题,也不受内存模型的影响

       Java线程之间的通信有Java内存模型(JMM)控制,JMM决定了一个线程对共享变量的写入何时对另一个线程可见。Java Memory Model(Java内存模型), 围绕着在并发过程中如何处理可见性、原子性、有序性这三个特性而建立的模型。

 JMM的抽象示意图如下:

         线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在,它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

备注:

        对于JMM和JVM本身的内存模型,参照《深入理解java虚拟机》的解释,这两者没有关系。如果一定要勉强对应,那么从变量、主内存、工作内存的定义来看,主内存主要对应java堆中的对象实例数据部分,而工作内存则对应于虚拟机栈中的部分数据。从更低层次上说,主内存就是物理内存,而为了获取更好的读写速度,虚拟机(甚至是硬件本身的优化措施)可能会让工作内存优先存储于寄存器和高速缓存中,因为运行时主要访问---读写的是工作内存。

        本地内存并不是把主内存中所有对象拷贝出来,而是存放的该线程使用到的变量。线程对变量的所有操作(读取、赋值等)都只能在工作内存中,完了之后写入主内存中。

         其实volatile修饰的变量也是在工作内存中有副本的,所以在各个线程的工作内存中,volatile变量也是可能存在互不相同的时候。只不过volatile变量在每次使用之前都会先刷新,也就是说对于执行引擎而言是看不到刷新前的不一致现象的。这样的操作顺序很特殊,让volatile变量看起来就像是在直接操作主内存一样。

变量在内存中的工作过程:

内存间的交互操作

      lock、unlock、read、load、use、assign、store、write,8个操作具有原子性(除开64位的数据类型:long和double需要划分两次32的操作外)

      lock和unlock区别:lock作用于主内存变量,把一个变量标识为一条线程独占的状态;unlock也作用于主内存变量,把一个处于锁定状态的变量释放

     read和load区别:read作用于主内存变量,将它的值从主内存中取出并传输至线程的工作内存load作用于工作内存变量,将read读出的变量值放入工作内存的变量副本中

      use和assign的区别:use作用于工作内存变量,把它的值传递给执行引擎,每当虚拟机遇到需要使用这个变量的字节码指令的时候就会执行这个操作;assign也作用于工作内存变量,把一个从执行引擎接收到的值赋给工作内存变量,每当虚拟机遇到给这个变量赋值的字节码指令的时候就会执行这个操作。

     store和write区别:store作用于工作内存变量,将它的值传输到主内存write作用于主内存变量,将store传过来的值放入主内存变量中。

volatile关键字(没想到一个volatile关键字这么多内容):

        保证此变量对所有线程是立即可见的、禁止指令重排序,保证代码的执行顺序和程序的执行顺序一致

详细分析:

        假设有线程T和volatile变量V和volatile变量W,那么在进行read、load、use、assign、store、write的时候会有如下的规则:

        同一个线程操作同一个变量时,use的前一个动作必须是load,同时只有在马上要use的时候才能进行load(这就保证了每次使用变量前,都能从主内存得到最新的值)

        同一个线程操作同一个变量时,store的前一个动作必须是assign,同时只有在马上要store的时候才能进行assign(这就保证了每次变量一更改,主内存马上就能得到更新)

        假设动作A是线程T对变量V实施的use或者assign,假设动作F是和A相关的load或者store,假设动作P是和F相关的read或者write;假设动作B是线程T对变量W实施的use或者assign,假设动作G是和B相关的load或者store,假设动作Q是和G相关的read或者write;那么,如果A先于B,则P先于Q(也就是此时不会出现指令重排序,代码的执行顺序与程序的执行顺序一致)。

volatile和synchronized对比

        关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰变量,而synchronized可以修饰方法和代码块。之前一直说尽量少用synchronized修饰方法,但是现在新版的Java之后,这个效率得到了很大程度提升。

        多线程访问volatile不会发生阻塞、而synchronized会出现阻塞

        volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数锯做同步

        volatile解决的是变量在多个线程之间的访问性,synchronized解决的是多个线程之间访问资源的同步性

内存可见性:

        内存可见性是指当一个线程修改了某个变量的值,其它线程总是能知道这个变量变化。也就是说,如果线程 A 修改了共享变量 V 的值,那么线程 B 在使用 V 的值时,能立即读到 V 的最新值。

内存可见性的解决方案:

        加锁synchronized)或使用volatile关键字。

为什么加锁可以保证内存的可见性

        1.线程获取锁。

        2.清空工作内存

        3.从主内存拷贝共享变量最新的值到工作内存为副本

        4.执行代码

        5.将修改后的副本的值刷新回主内存

        6.线程释放锁。

为什么volatile可以保证内存的可见性

        使用 volatile 修饰共享变量后,每个线程要操作变量时会从主内存中将变量拷贝到本地内存作为副本,当线程操作变量副本并写回主内存后,会通过CPU 总线嗅探机制告知其他线程该变量副本已经失效,需要重新从主内存中读取。

        volatile 保证了不同线程对共享变量操作的可见性,也就是说一个线程修改了 volatile 修饰的变量,当修改后的变量写回主内存时,其他线程能立即看到最新值

volatile的原子性问题:

        所谓的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行

        在多线程环境下,volatile 关键字可以保证共享数据的可见性,但是并不能保证对数据操作的原子性。也就是说,多线程环境下,使用 volatile 修饰的变量是线程不安全的。

        要解决这个问题,我们可以使用锁机制,或者使用原子类(如 AtomicInteger)。

        AtomicInteger中保存的值value是用volatile修饰的,但是这并不能保证原子性。AtomicInteger中保证原子性是通过使用的CAS算法实现。例如:atomicInteger.incrementAndGet()方法:

这里需要特别说明一下:

        volatie修改的变量i在多个线程中执行i++操作是不能保证数据的正确性的!!!!

        i++不是一个原子操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本火锅店点餐系统采用Java语言和Vue技术,框架采用SSM,搭配Mysql数据库,运行在Idea里,采用小程序模式。本火锅店点餐系统提供管理员、用户两种角色的服务。总的功能包括菜品的查询、菜品的购买、餐桌预定和订单管理。本系统可以帮助管理员更新菜品信息和管理订单信息,帮助用户实现在线的点餐方式,并可以实现餐桌预定。本系统采用成熟技术开发可以完成点餐管理的相关工作。 本系统的功能围绕用户、管理员两种权限设计。根据不同权限的不同需求设计出更符合用户要求的功能。本系统中管理员主要负责审核管理用户,发布分享新的菜品,审核用户的订餐信息和餐桌预定信息等,用户可以对需要的菜品进行购买、预定餐桌等。用户可以管理个人资料、查询菜品、在线点餐和预定餐桌、管理订单等,用户的个人资料是由管理员添加用户资料时产生,用户的订单内容由用户在购买菜品时产生,用户预定信息由用户在预定餐桌操作时产生。 本系统的功能设计为管理员、用户两部分。管理员为菜品管理、菜品分类管理、用户管理、订单管理等,用户的功能为查询菜品,在线点餐、预定餐桌、管理个人信息等。 管理员负责用户信息的删除和管理,用户的姓名和手机号都可以由管理员在此功能里看到。管理员可以对菜品的信息进行管理、审核。本功能可以实现菜品的定时更新和审核管理。本功能包括查询餐桌,也可以发布新的餐桌信息。管理员可以查询已预定的餐桌,并进行审核。管理员可以管理公告和系统的轮播图,可以安排活动。管理员可以对个人的资料进行修改和管理,管理员还可以在本功能里修改密码。管理员可以查询用户的订单,并完成菜品的安排。 当用户登录进系统后可以修改自己的资料,可以使自己信息的保持正确性。还可以修改密码。用户可以浏览所有的菜品,可以查看详细的菜品内容,也可以进行菜品的点餐。在本功能里用户可以进行点餐。用户可以浏览没有预定出去的餐桌,选择合适的餐桌可以进行预定。用户可以管理购物车里的菜品。用户可以管理自己的订单,在订单管理界面里也可以进行查询操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值