JVM指令重排的例子

package player.kent.chen.learn.reorder;

import java.text.MessageFormat;

//转账类
public class Transfer {

    private int              amountA = 100;
    private int              amountB = 100;
    private volatile boolean committed;

    
    //转账动作
    public void doTransfer() {
        amountA = amountA - 50;
        amountB = amountB + 50;
        committed = true;
    }


    //转账结束时打印两个账户的值
    public void printAccountsWhenDone() {
        if(!committed) { //转账未结束则等待。
            return; 
        }
        System.out.println(MessageFormat.format("amountA = {0}, amountB = {1}", amountA, amountB));
    }

    public static void main(String[] args) {
        final Transfer transfer = new Transfer();
        Thread t1 = new Thread(new Runnable() {

            public void run() {
                transfer.doTransfer();
            }
        });

        Thread t2 = new Thread(new Runnable() {

            public void run() {
                while(true){
                    transfer.printAccountsWhenDone();
                }                
            }
        });

        t2.start();   //线程2等转账结束后打印账户节余
        t1.start();   //线程1处理转账
    }

}

最后打印的账户节余是多少?

初看应该是 A = 50 和 B = 100.

可是,按指令重排原理, 下面三句话可能被CPU按别的顺序执行:

        amountA = amountA - 50;
        amountB = amountB + 50;
        committed = true;

比如,可能重排成

        amountA = amountA - 50;
        committed = true;
        amountB = amountB + 50;

这时候打印出来的值可能就是 A = 50, B = 100 .

要解决这个问题,就要给doTransfer()和printAccountsWhenDone()都加上synchronized关键字,当t1执行doTransfer时t2必须等待,等t1执行完后t2再去获取A和B的值,即使doTransfer()内部有指令重排,也不会有问题。

Leave a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.