• 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对象来解决。

  • 相关阅读:
    LA 3026 Period
    Touch
    Uva 11762 Race to 1
    清北学堂模拟赛d1t2 火柴棒 (stick)
    清北学堂模拟赛d1t1 位运算1(bit)
    常州模拟赛d8t2 绘画
    常州模拟赛d8t1 友好数对
    常州模拟赛d5t3 appoint
    常州模拟赛d7t1 亲戚
    常州模拟赛d7t3 水管
  • 原文地址:https://www.cnblogs.com/soar-hu/p/6737966.html
Copyright © 2020-2023  润新知