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

posted @ 2017-04-20 11:49  soar_hu  阅读(1143)  评论(0编辑  收藏  举报