• 一个关于Java 多线程问题的知识点


    这个程序运行结果会是什么?

    public class Main {static class ListAdd {
            private static List list = new ArrayList();
    
            public void add() {
                list.add("baoer");
            }
    
            public int size() {
                return list.size();
            }
    
        }
    
        public static void main(String[] args) throws IOException {
    
            final ListAdd list = new ListAdd();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for (int i=0 ; i < 10; i++) {
                            Thread.sleep(500);
                            list.add();
                            System.out.println("线程:" + Thread.currentThread().getName() + " 添加了一个元素" );
                        }
    
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"T1").start();
    
    
           new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
    
                            if (list.size()== 5) {
                                System.out.println("当前线程收到通知 " + Thread.currentThread().getName() + "  list.size=5线程停止");
                                throw new RuntimeException();
                            }
    
                    }
                }
            }, "T2").start();
    
           // fun();
    
        }

    如果知道ArrayList不是线程安全的也许答案就是线程T1运行结束,T2一直执行下去不会抛出异常而结束。事实结果也确是这样。但这样的执行结果背后却值得深思。

    问题1:这是因为有可能T2线程某次读入缓存的size为4,但下一次读入缓存的数字是6,所以永远进入不了if.

        但由于线程T1每次add之后都sleep 500 毫秒所以这种可能不存在。

    问题2:在T2 if(list.size() == 5) 之前将size放入一个Hashset发现Hashset中只有一个值 0 。这说明size根本没有从主内存中刷新到T2工作内存中,为什么主存中size值都更新了还不刷新到工作内存中呢?不是有缓存一致性吗?

        原因就是 T2中的size根本没有在T2的缓存中!这是编译器干的事! 编译器发现是一个while(true),并且要频繁使用size,就会把size放在寄存器中提高访问速度,缓存不保存size。所以即使有缓存一致性size永远无法更新。

    问题3: 为什么list是volatile 就会得到正确结果?

        对于volatile变量 编译器不会把它放入寄存器中,在缓存中volatile 可以保证可见性,并且根据happens-before规则 volatile 的读取一定在写入之后。

    问题4: 为什么在if判断前加 System.out.println(list.size()); 也会的到正确结果?

       通过查看System.out.println 源码发现执行输出语句时要加同步锁,

        JMM关于synchronized的两条规定:

        1. 线程解锁前,必须把共享变量的最新值刷新到主内存中
        2. 线程加锁时,先清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值。

        由于清空工作内存中的值所以寄存器中的值也失效了,虽然此时值没在工作内存中,但也寄存器也会刷新再从工作内存中读取。

    问题5: 为什么在if判断前加Thread.sleep(0)或者Thread.yield();也会的到正确结果?

        这涉及到了线程的上下文切换,一但切换上下文工作内存中的就值就会失效,系统保存了线程的状态,下次切换回来时重新从内存中读值。

  • 相关阅读:
    合并一个二维数组中相同项,其他数量则相加
    清除空值
    数组的一维下标换为指定的key值
    二维数组排序
    word输出
    解决Zend加密的PHP页面出现Fatal error: Incompatible file format: The encoded file has format major ID 1, whereas the Loader expects 4 in
    nginx反向代理substitutions4nginx模块实现替换字符盗站 nginx.conf配置
    ubuntu 编译安装nginx 并添加substitutions4nginx模块
    windows 2008 R2 wincache 不稳定
    php5.5及php5.6 wincache无法启用问题
  • 原文地址:https://www.cnblogs.com/mibloom/p/9475252.html
Copyright © 2020-2023  润新知