纠正关于线程的错误理解

这里有个这样的程序,设计个简单的银行,假设某家银行,它可接受顾客的汇款,每做一次汇款,便可计算出汇款的总额。现在有两个客户,都对银行里同一个账户进行存钱取钱操作。测试synchronized关键字的作用。

现在上代码:

主要测试类:

/**
 * 这个类就是拿来做测试跑程序用的
 * @author 85060
 *
 */
public class MainTest2 {
    public static void main(String[] args) {
        EasyBank theBank = new EasyBank();//创建一个银行
        Client1 c1 = new Client1("c1", theBank);//创建客户1,是个runnable
        Client2 c2 = new Client2("c2", theBank);//创建客户2,是个runnable
        Thread c1Thread = new Thread(c1);//创建一个客户1的线程
        Thread c2Thread = new Thread(c2);//创建一个客户2的线程
        c1Thread.start();
        c2Thread.start();
        theBank.seeTheAccount();//在某一时刻查看银行账户的存款
        try {
            Thread.sleep(15000);//主线程睡个15秒
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        theBank.seeTheAccount();//15秒后再看一次(即最后银行的账户余额)
    }
}

银行类:

/**
 * 这个类是个简单的银行
 * @author 85060
 *
 */
public class EasyBank {
    public static double account = 0;
    
    public synchronized void deposit(Client client, double amount) {//存钱,带锁的,调用这个方法对象会被锁住
        account = account + amount;
        System.out.println("Client: "+client.name+"  The deposit operation has been done,the total account now is "+account);
    }
    
    public synchronized void withdraw(Client client, double amount) {//取钱,也是带锁的,如果没钱拿就等个10秒,看得出同步的效果
        if(account==0) {
            System.out.println("Client: "+client.name+"   Sorry,you don't have any money to withdraw!");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } else {
            account = account - amount;
            System.out.println("Client: "+client.name+"  The withdraw operation has been done,the total now is "+account);
        }
    }
    
    public void seeTheAccount() {
        System.out.println("The account is "+account);
    }
}

抽象客户类和两个客户的runnable类:

/**
 * 这个类是取钱的客户
 * 
 * @author 85060
 * 
 */
public abstract class Client {
    String name;
    EasyBank bank;
    
    public Client(String name, EasyBank bank) {
        this.name = name;
        this.bank = bank;
    }

    public void toDeposit(double amount) {
        bank.deposit(this, amount);
    }

    public void toWithDraw(double amount) {
        bank.withdraw(this, amount);
    }
}

class Client1 extends Client implements Runnable {
    
    public Client1(String name, EasyBank bank) {//存三次钱
        super(name, bank);
        // TODO Auto-generated constructor stub
    }
    
    @Override
    public void run() { // 
        // TODO Auto-generated method stub
        try {
            Thread.sleep(10);
            toDeposit(100);
            Thread.sleep((int) ((Math.random() * 1000)));
            toDeposit(100);
            //Thread.sleep((int) ((Math.random() * 1000)));// 休眠0~1秒
            toDeposit(100);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }// 休眠0~1秒
    }
}

class Client2 extends Client implements Runnable {//取两次钱存1次钱
    
    public Client2(String name, EasyBank bank) {
        super(name, bank);
        // TODO Auto-generated constructor stub
    }
    
    @Override
    public void run() { 
        // TODO Auto-generated method stub
        try {
            //Thread.sleep(10);
            toWithDraw(100);
            //Thread.sleep((int) ((Math.random() * 1000)));
            toWithDraw(100);
            //Thread.sleep((int) ((Math.random() * 1000)));// 休眠0~1秒
            toDeposit(100);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }// 休眠0~1秒
    }
}

银行的存钱deposit方法和拿钱withdraw方法都加了synchronized关键字。

一开始想着说,线程2(客户2)先访问嘛(因为可客户1先睡了个几毫秒,所以客户2会先拿到锁),然后客户2会走完他所有的run()方法后再到客户1走他的run方法。   就想着说理想的输出应该是Client 2: xxxxxxxxxxx    Client2:.............  2:...........   .......................   Client1:.............   Client1:.............

结果是输出竟然是Client1和Client2交错出现的,然后我就很奇怪了。两个方法都带锁啊,不是会锁住对象的吗???

然后发现每个线程都是是sleep()方法之后,后面的输出就换线程了。

奇怪了?sleep不是只是暂停线程,但它不会把锁抛掉啊?!

后面查了好久,测试了几次才意识到,这里的锁住对象,是方法的这块代码锁住对象。一个线程的run()方法里执行了那么多次deposit()和withdraw()方法。只是说当一个线程执行比如说deposit()方法的时候,其他线程不可能访问这个对象里的带有synchronized的方法。    但当线程执行完这个带锁的方法后,就自然会把锁放出,这个时候就再两个线程去抢锁。    然后如果你这个时候如果线程睡了sleep()了一会儿的话,锁肯定会被别的线程抢走啊!!所以就会出现交错出现的情况。

原文地址:https://www.cnblogs.com/wangshen31/p/7666260.html