A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 小蜀哥哥 于 2018-10-30 13:13 编辑

     问题一:为什么要学习JMM内存模型
     在我们学习Java的第一天就了解到Java是一门通过JVM虚拟机跨平台的语言,同时Java也是一门具有多个线程同时处理能力的编程语言,那么在多个线程并发的情况,我们的Java底层是如何对这些线程进行处理,且又如何保证线程的安全性,这就是学习JMM内存模型的原因以及目的
     并发编程分类
    并发编程解决的问题是多个线程在怎么样交互数据,简单的说,就是多个线程在处理同一个变量时,如何进行信息的沟通,当下流行的一共有两种通信的机制
      1.共享内存
      在这种内存模型下,会产生工作内存(将共享数据加载到工作内存中来进行操作,保证数据的高效性)和主内存(存放共享数据),工作内存和主内存之间通过read 和 write 来进行数据的交流
   2.消息传递
      这种模型是没有共享数据,线程之间必须通过明确的发送消息来显式进行通信。


内存模型的工作方式
      Java采用的是共享内存模型
      正如上文所介绍的那样,Java会将共享的数据放置到主内存中,当多个线程操作这个共享变量时,会将对应的内容加载到自己的工作内存中来,也就是说,有多少个线程就有多少个工作内存
      假设现在有内存A和B有主内存中共享变量x的副本。初始时,这三个内存中的x值都为0。线程A在执行时,把更新后的x值(假设值为1)临时存放在自己的本地内存A中。当线程A和线程B需要通信时,线程A首先会把自己本地内存中修改后的x值刷新到主内存中,此时主内存中的x值变为了1。随后,线程B到主内存中去读取线程A更新后的x值,此时线程B的本地内存的x值也变为了1。
      从整体来看,这两个步骤实质上是线程A在向线程B发送消息,而且这个通信过程必须要经过主内存。JMM通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性保证。内存模型中值得注意的三个点
      1.    内存可见性
   内存可见性的问题指的就是在工作方式中介绍到当A修改了工作内存中的数据,此时B去操作自己工作内存中的数据时, A需要将数据写回到主内存中,B再次从主内存中读取数据,但这有可能情况并不是那么美好,若B没有从主内存中读取数据,那么此时B中的数据和A中的数据此刻将变得没有意义,所以我们将这重在修改了各自工作内存后,没有重新从主内存中读取数据的行为,称之为内存可见性问题,A和B彼此之间数据不可见     
[Java] 纯文本查看 复制代码
 public class VolatileTest {
                   public static void main(String[] args) {
                   MyThread mt = new MyThread();
                   new Thread(mt).start();
                   while(true){
                             if(mt.isFlag()){
                                     System.out.println("------");
                                     break;
                            }        
                    }
            }
        }
        class MyThread implements Runnable{
                private volatile boolean flag = false;
                public boolean isFlag() {
                        return flag;
                }
                public void setFlag(boolean flag) {
                        this.flag = flag;
                }
                @Override
                public void run() {
        
                        try {
                                Thread.sleep(200);
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }
                        flag  = true;
                        System.out.println("flag =  "+isFlag());
                }
        }


在本案例中,我们希望看到的执行结果是,程序会在200毫秒后打印----,同时程序结束,但真正的情况是本程序永远不会结束,原因是while操作是属于底层代码操作,执行效率非常之快,会导致main线程都来不及从主内存中抓取被MyThread修改后的flag,导致main线程一直在读取自己工作内存中的数据,而自己工作内存中的数据一直是false,故而程序不会结束
   解决内存可见性问题的手段有很多synchronized、Lock、final、volitile都能够解决内存可见性问题

2.重排序问题
   重排序问题,指的是一个线程在不影响编译结果的情况下,会按照JVM喜爱的方式去对程序进行顺序上的调整
   用一段伪代码来展示
[AppleScript] 纯文本查看 复制代码
 main(){
	 int i =10;   //1
         int j =20;   //2
         int flag = true;  //3
         int temp = i * j ;//4
     }

   在这段程序中,我们按照代码的顺序,编号1,2,3,4 我们能够看到只要4在1,2 之后,那么这个程序无论是哪种编译情况都不会影响最终程序的执行结果  ,如: 2,1,4,3  1,3,2,4 ,在单线程情况,这种重排序的方案并没有任何问题,但如果出现了多线程程序
[Java] 纯文本查看 复制代码
class MyExample(){
           private boolean flag = false;
           int i =5;
           int j =5;
   
           read(){
              i =10;                        //1
              j = 20;                     //2
              flag = true;                //3
           }
               write(){
                           while(flag){
                  int temp = i * j;
              }
                        
           }
      }

    我们会发现如果read是一条线程,write是一条线程,那么在单独考虑read线程的情况下, read线程无论是按照1,2,3排序,还是3,2,1或者是任意方式排序,都不会对程序产生任何影响,但此时若考虑到write线程,若read线程时1,2,3 那么write 就是 200,而如果按照3,2,1变成,那么write线程的结果就是25,所以JMM的重排序是一个我们需要考虑的地方,解决的方案是 synchronized、Lock 、volitile 除此之外 Java 内存模型通过 happens-before 原则如果能推导出来两个操作的执行顺序就能先天保证有序性,否则无法保证, happens-before中定义了8种情况,在满足这8种情况,JVM不会对代码进行重排序
   具体规则请自行查询

    3.原子性问题
   
[Java] 纯文本查看 复制代码
    所谓的原子性操作的含义是指该操作是不可分割的,比如我们通常写的  i = 0; int i = i++;
在底层是由三步组成 
int temp = i;
temp = temp + 1;
int i = temp;

  使用volitile关键字 是无法保证volitile关键字的,我们可以使用synchronized关键字,以及atomicInteger来解决这样的问题,而atomicInteger底层采用的是cas算法,不属于我们本次的讨论范围

总结
         相信同学们在学习完本章内容之后一定对JMM内存模型有了一定的了解,学习是没有止境的,光去看表面是不能解决问题的,在学习到一定阶段之后,这些内容都需要同学们去思考,谢谢大家。

2 个回复

倒序浏览
沙发沙发
回复 使用道具 举报
厉害厉害
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马