谈谈JMM内存可见性

博客探讨了Java与C语言中线程并发的问题,通过示例代码展示了线程A等待flag变量变化而线程B修改flag的情况。在不同编译模式下,程序表现各异,说明了线程同步、编译优化对程序运行的影响。在混合编译模式下线程可能无法正确同步,而加入`synchronized`关键字或在C语言中使用原始的互斥锁可以确保线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 问题引出

附上代码

public class Test01 {

    private boolean flag = false;

    public void refresh() {
        flag = true;
    }

    public void load() {
        while (!flag) {
        }
        System.out.println("out");
    }

    public static void main(String[] args) throws InterruptedException {
        Test01 test01 = new Test01();
        new Thread(test01::load).start();

        Thread.sleep(3000L);

        new Thread(test01::refresh).start();
    }
}

两个线程,一个线程A等待flag变量变为false,另一个线程B在睡眠三秒后修改flag为false,按道理这个线程A应该停止,毕竟flag的值变为了false。但是出现了两个奇怪的现象:

1、当开启MIXED混合编译的时候停不下来

2、当开启XINT解释模式时可以停下来

3、当在中间加入了Synchronized代码块后可以停下来

  • 我们从c语言的角度看看结果如何:
#include<stdio.h>

#include<pthread.h>

int flag = 0;

int count = 1;

void *modify(void *arg) {

    printf("modify start!\n");

    flag = 1;

    printf("modify end!\n");

    pthread_exit((void *) 0);

};

void *sum(void *arg) {

    printf("sum start!\n");

    while (!flag) {

        count++;

    }

    printf("sum end!\tcount = %d\n", count);

    pthread_exit((void *) 0);

};

int main() {

    pthread_t modifyPid;

    pthread_t sumPid;

    if ((pthread_create(&sumPid, NULL, sum, NULL)) != 0) {

        printf("pthread_create err\n");

        return -1;

    }

    if ((pthread_create(&modifyPid, NULL, modify, NULL)) != 0) {

        printf("pthread_create err\n");

        return -1;

    }

    if (pthread_join(modifyPid, NULL) != 0) {

        printf("pthread_join err\n");

        return -1;

    }

    if (pthread_join(sumPid, NULL) != 0) {

        printf("pthread_join err\n");

        return -1;

    }
}

结果:
sum start!
modify start!
modify end!
sum end! count = 14341

结果竟然停下来了。。。什么鬼!

先别激动, 先看看我的编译命令

gcc test.c -lpthread

来, 我们加点料

gcc -O4 test.c -lpthread
结果:
sum start!
modify start!
modify end!

哎呀! 停不下来了。

一言不合上汇编, 我们看看编译器都做了些什么

gcc -S test.c -lpthread

modify: // modify的代码段
.LFB2:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movq    %rdi, -8(%rbp)
        movl    $.LC0, %edi
        call    puts
        movl    $1, flag(%rip)  // 将1放入flag的内存地址中,然后退出
        movl    $.LC1, %edi
        call    puts
        movl    $0, %edi
        call    pthread_exit

......
sum:   // sum的代码段
.LFB3:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movq    %rdi, -8(%rbp)
        movl    $.LC2, %edi
        call    puts
        jmp     .L3
.L4:
        movl    count(%rip), %eax
        addl    $1, %eax
        movl    %eax, count(%rip)
.L3:
        movl    flag(%rip), %eax
        testl   %eax, %eax
        je      .L4
        movl    count(%rip), %eax
        movl    %eax, %esi
        movl    $.LC3, %edi
        movl    $0, %eax
        call    printf
        movl    $0, %edi
        call    pthread_exit
        .cfi_endproc
.LFE3:
        .size   sum, .-sum
        .section        .rodata
.LC4:
        .string "pthread_create err"
.LC5:
        .string "pthread_join err"
        .text
        .globl  main
        .type   main, @function
modify:
.LFB14:
        .cfi_startproc
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        movl    $.LC0, %edi
        call    puts
        movl    $.LC1, %edi
        movl    $1, flag(%rip)
        call    puts
        xorl    %edi, %edi
        call    pthread_exit
        .cfi_endproc
.LFE14:
        .size   modify, .-modify
        .section        .rodata.str1.1
.LC2:
        .string "sum start!"
.LC3:
        .string "sum end!\tcount = %d\n"
        .text
        .p2align 4,,15
        .globl  sum
        .type   sum, @function
sum:
.LFB15:
        .cfi_startproc
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        movl    $.LC2, %edi
        call    puts
        movl    flag(%rip), %eax
        testl   %eax, %eax
        jne     .L9
.L6:
        jmp     .L6  // 死循环
.L9:
        movl    count(%rip), %esi
        movl    $.LC3, %edi
        xorl    %eax, %eax
        call    printf
        xorl    %edi, %edi
        call    pthread_exit
        .cfi_endproc
.LFE15:
        .size   sum, .-sum
        .section        .rodata.str1.1
.LC4:
        .string "pthread_create err"
.LC5:
        .string "pthread_join err"
        .section        .text.startup,"ax",@progbits
        .p2align 4,,15
        .globl  main
        .type   main, @function

待续。。。。。。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值