zoukankan      html  css  js  c++  java
  • java多线程中关于原子操作

    各种不同的情况保证数据的正确性,完整性。

    public class TestMultiThread implements Runnable {
        private static int i;
        private static volatile Integer vi = 0;
        private static AtomicInteger ai = new AtomicInteger();
        private static Integer si = 0;
        private static Integer ia = 1;
        private static int ri;
        private static AtomicInteger flag = new AtomicInteger();
        private static Lock lock = new ReentrantLock();   //如果lock不加static关键字时,那么每一个线程将锁住的是具体的对象,
                                                          //而不是这个类。就会照成数据的不正确性
    
        @Override
        public void run() {
            for (int k = 0; k < 200000; k++) {
                i++;    //不是原子操作,此处含有三步(取得i的值,i+1,把i+1后的结果赋值给i)结果将有异常。当一条线程执行到i+1时,还没有赋值,另一线程i+1并赋值。
                vi++;    //同上,②解释
                ai.incrementAndGet();    //可以用原子方式更新的 int 值。以原子方式将当前值加 1.它只有一步操作,随便哪个线程执行都将只有这一步。
            
                // synchronized(si){
                                            // 此方法会照成si的值不对,是应为同步语句锁定的是对象,用Integer作为对象锁来使用,这本身没有任何问题。
                // si++;                    // 但同步体里的操作偏偏是更改当前的锁对象,结果就是前的对象锁其实已经改变了,不是针对唯一的一个对象加锁,所以会出现问题。
                                            // 也因此,换一个公共对象就能解决问题。
                                            // 在++操作中其实有一个赋值操作,这时对象已经改变,或者说是非原子操作        
                // }
                synchronized (ia) {   //用不变的ia作为锁对象就能保证数据的正确性
                    si++;
                }
                lock.lock();   //此处的lock锁必须为static的
                try {
                    ri++;
                } finally {
                    lock.unlock();
                }
            }
            flag.incrementAndGet();
        }
    
        public static void main(String[] args) throws InterruptedException {
            TestMultiThread t1 = new TestMultiThread();
            TestMultiThread t2 = new TestMultiThread();
            ExecutorService exec1 = Executors.newCachedThreadPool();
            ExecutorService exec2 = Executors.newCachedThreadPool();
            exec1.execute(t1);
            exec2.execute(t2);
            while (true) {
                if (flag.intValue() == 2) {
                    System.out.println("i>>>>>" + i);
                    System.out.println("vi>>>>>" + vi);
                    System.out.println("ai>>>>>" + ai);
                    System.out.println("si>>>>>" + si);
                    System.out.println("ri>>>>>" + ri);
                    break;
                }
                Thread.sleep(50);
            }
        }
    }

    结果:

    i>>>>>398950
    vi>>>>>386295
    ai>>>>>400000
    si>>>>>400000
    ri>>>>>400000

    解释②:

    在 java 垃圾回收整理一文中,描述了jvm运行时刻内存的分配。其中有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈,

    线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存

    变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,

    在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。

  • 相关阅读:
    POJ-1189 钉子和小球(动态规划)
    POJ-1191-棋盘分割(动态规划)
    Java实现 LeetCode 730 统计不同回文子字符串(动态规划)
    Java实现 LeetCode 730 统计不同回文子字符串(动态规划)
    Java实现 LeetCode 729 我的日程安排表 I(二叉树)
    Java实现 LeetCode 729 我的日程安排表 I(二叉树)
    Java实现 LeetCode 729 我的日程安排表 I(二叉树)
    Java实现 LeetCode 728 自除数(暴力)
    Java实现 LeetCode 728 自除数(暴力)
    Java实现 LeetCode 728 自除数(暴力)
  • 原文地址:https://www.cnblogs.com/sanhuan/p/4736988.html
Copyright © 2011-2022 走看看