JAVA是如何运行的三线程模型

JVM线程内存模型

根据上述两篇文章的说明,大概知道jvm是如何分配内存并且运行起来的。

现在我们来看既然对于JVM的内存模型来说,方法区和堆是线程共享的 但是对于jvm栈,栈帧,计数器等是线程独享的,很显然,当两个线程如果同时操作方法区中的静态变量n,为什么会产生并发问题?那如何解决并发问题?

首先来看下JVM的线程模型

首先为什么线程模型会长这个样子?

在一般理解的情况下,线程是有其独享的内存在运行时如果需要操作共享变量需要将方法区存储的变量拿到线程内存中进行操作,操作完成之后赋值方法区变量。可是实际情况并非如此!随着cpu的快速发展,直接从内存上获取数据的效率低下,无法满足现在cpu的高速运转,于是每个cpu都会存在多级缓存机制,如下图所示

那cpu和线程又有什么关系呢?首先线程是如何运行的呢?拿单核的CPU举例:cpu在同一节点只能做一个操作,为了cpu有多任务处理的能力, 可以理解为将cpu时间划分为时间片,cpu在某一个时间片执行某一个线程,循环往复。

接着上述所说可以理解线程就是单个的cpu,那当cpu执行代码时首先会将用到的变量缓存到高速缓存区,然后当需要操作变量时会从高速缓存区取值而不是直接从内存地址上取值。

这种模式明显加快了数据访问速度,但是也产生了并发问题!

那接下来看一段代码

    public static boolean flag = false;
    public static void main(String[] args) throws InterruptedException {

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!flag){
                }
                System.out.println("线程1结束");
            }
        }).start();
        Thread.sleep(5000);
        new Thread(new Runnable() {
            @Override
            public void run() {
                flag = true;
                System.out.println("线程2结束");
            }
        }).start();
    }
}

当代码运行起来之后发现:当线程二结束之后,线程1仍然在while死循环!

那说明当线程二执行完后,虽然flag的值改变了 但是并没有同步到线程中,对于线程一来说flag的值仍然是false

接下来看另外一段代码

    public static int flag = 0;
    public static void main(String[] args) throws InterruptedException {

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    flag++;
                }
                System.out.println(flag);
                System.out.println("线程1结束");
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    flag++;
                }
                System.out.println(flag);
                System.out.println("线程2结束");
            }
        }).start();
    }

运行以上代码发现:运行完程序后我们的理论值应该需要flag = 20000,而实际得到的值大部分都不是我们的期望值。

由此可见,当flag值改变时,flag值会重新写入主内存区,当需要操作时再从主内存读取值到高速缓存区。

下面先来看线程内存交互图:

 

Java内存模型定义了八种操作

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

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

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

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

use(使用):作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎;

assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存中的变量;

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

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

 

针对第一部分代码:

对于线程一来说,当线程二将最新的值变量值write到主内存中时,线程一是没有感知的,也就是说线程一并不知道何时read内存。

那如何能通知到线程一呢?这是否需要关键字Volatile?

 

针对第二部分代码:

对于任何一个线程来说例如当线程一将flag=2的值 write到主内存时,此时线程二可能正要将flag=2 write 到主内存中。那对于flag来说其实已经累加过一次应该是3,所以就会出现并发问题使得最后的值不等于20000.那如果我们对flag的内存加锁,同时只允许一个线程对其操作会如何呢?这是否需要关键字synchronized?

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值