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