zoukankan      html  css  js  c++  java
  • java多线程基本概述(十八)——分析i++操作

    下面用javap分析一下为什么i++不安全

    /**
     * Created by huaox on 2017/4/20.
     *
     */
    public class TestIncrement {
        private int i = 0;
    
        void f1(){
            i++;
        }
        void f2(){
            i+=3;
        }
    }

    执行 javap -c TestIncrement 得到的结果为:

    Compiled from "TestIncrement.java"                                                      
    public class TestIncrement {                                                            
      public TestIncrement();                                                               
        Code:                                                                               
           0: aload_0                                                                       
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V     
           4: aload_0                                                                       
           5: iconst_0                                                                      
           6: putfield      #2                  // Field i:I                                
           9: return                                                                        
                                                                                            
      void f1();                                                                            
        Code:                                                                               
           0: aload_0                                                                       
           1: dup                                                                           
           2: getfield      #2                  // Field i:I                                
           5: iconst_1                                                                      
           6: iadd                                                                          
           7: putfield      #2                  // Field i:I                                
          10: return                                                                        
                                                                                            
      void f2();                                                                            
        Code:                                                                               
           0: aload_0                                                                       
           1: dup                                                                           
           2: getfield      #2                  // Field i:I                                
           5: iconst_3                                                                      
           6: iadd                                                                          
           7: putfield      #2                  // Field i:I                                
          10: return                                                                        

    方法f1()中,

    1:(getField)进行了获取filed i的操作,--------code 2

    2:然后取常量值1,                             --------code 5

    3:再把取到的值1加到代号2取到的值上,  --------code 6

    4:再把求和后得到的值放到field i字段中   --------code 7

    5:最后返回值               --------code 10

    可以看到执行一个i++的操作会经历这一些操作,所以再这些操作中可能会被其他的线程读取到。所以不是原子安全的

    如何再锁线程环境下使用一个线程不安全的类

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * Created by huaox on 2017/4/20.
     *
     */
    
    class Pair{
        private int x,y;
        Pair(int x, int y) {
            this.x = x;
            this.y = y;
        }
        Pair() {this(0,0);}
        int getX(){return x;}
        int getY(){return y;}
        void incrementX(){x++;}
        void incrementY(){y++;}
    
        @Override
        public String toString() { return "x: "+x+", y:"+y;}
        public class PairValueNotEqualsException extends RuntimeException{
            PairValueNotEqualsException() {
                super("pair value is not equals: "+ Pair.this);
            }
        }
        void checkState(){
            if(x!=y)
                throw new PairValueNotEqualsException();
        }
    }
    
    abstract class PairManager{
        AtomicInteger checkCounter = new AtomicInteger(0);
        Pair pair = new Pair();
        private List<Pair> storage = Collections.synchronizedList(new ArrayList<>());
        synchronized  Pair getPair(){
            return new Pair(pair.getX(), pair.getY());
        }
        void store(Pair pair){
            storage.add(pair);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public abstract void increment();
    }
    
    class PairManager1 extends PairManager{
        @Override
        public synchronized void increment() {
                pair.incrementX();
                pair.incrementY();
                store(getPair());
        }
    }
    
    class PairManager2 extends PairManager{
        @Override
        public  void increment() {
            Pair temp = null;
            synchronized (this){
                pair.incrementX();
                pair.incrementY();
                temp = getPair();
            }
            store(temp);
        }
    }
    class  PairManipulator implements Runnable{
        PairManager pairManager;
    
        PairManipulator(PairManager pairManager) {
            this.pairManager = pairManager;
        }
    
        @Override
        public void run() {
                while (true)
                    pairManager.increment();
        }
    
        @Override
        public String toString() {
            return "Pair: "+pairManager.getPair()+" checkCounter: "+pairManager.checkCounter.get();
        }
    }
    
    class  PairChecker implements Runnable{
        PairManager pairManager;
    
        PairChecker(PairManager pairManager) {
            this.pairManager = pairManager;
        }
    
        @Override
        public void run() {
            while (true){
                pairManager.checkCounter.incrementAndGet();
                pairManager.getPair().checkState();
            }
        }
    
        @Override
        public String toString() {
            return "Pair: "+pairManager.getPair()+" checkCounter: "+pairManager.checkCounter.get();
        }
    }
    
    
    public class CriticalSection  {
        static void test(PairManager pairManager1,PairManager pairManager2){
            ExecutorService service = Executors.newCachedThreadPool();
            PairManipulator pm1 = new PairManipulator(pairManager1);
            PairManipulator pm2 = new PairManipulator(pairManager2);
            PairChecker pc1 = new PairChecker(pairManager1);
            PairChecker pc2 = new PairChecker(pairManager2);
    
            service.execute(pm1);
            service.execute(pm2);
            service.execute(pc1);
            service.execute(pc2);
            try {
                TimeUnit.MILLISECONDS.sleep(500);
                System.out.println("pairManager1: "+pm1+"  pariManager2: "+pm2);
                System.exit(0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            PairManager pairManager1 = new PairManager1();
            PairManager pairManager2 = new PairManager2();
            test(pairManager1,pairManager2);
        }
    }

    输出结果:

    pairManager1: Pair: x: 3, y:3 checkCounter: 35859  pariManager2: Pair: x: 4, y:4 checkCounter: 113213158
    
    Process finished with exit code 0

    上面的pair不是线程安全的,因为它的约束条件需要两个变量维护相同的值。且自增操作也不是线程安全的,并且没有被同步方法保护。所以不能保证Pair类再多线程环境下时线程安全的。比如就可以使用PairManager这个类来维护线程不安全的类。

    store方法将一个pair对象放在了  private List<Pair> storage = Collections.synchronizedList(new ArrayList<>()); 中,这是线程安全的。

    PairChecker用来作检查频率,一般而言,PairManager1不允许比PairManager2多。后者采用同步代码块,不加锁的时间长一些。也可以使用Lock对象来解决。

  • 相关阅读:
    Leetcode 349. Intersection of Two Arrays
    hdu 1016 Prime Ring Problem
    map 树木品种
    油田合并
    函数学习
    Leetcode 103. Binary Tree Zigzag Level Order Traversal
    Leetcode 102. Binary Tree Level Order Traversal
    Leetcode 101. Symmetric Tree
    poj 2524 Ubiquitous Religions(宗教信仰)
    pat 1009. 说反话 (20)
  • 原文地址:https://www.cnblogs.com/soar-hu/p/6737966.html
Copyright © 2011-2022 走看看