这里有个这样的程序,设计个简单的银行,假设某家银行,它可接受顾客的汇款,每做一次汇款,便可计算出汇款的总额。现在有两个客户,都对银行里同一个账户进行存钱取钱操作。测试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()了一会儿的话,锁肯定会被别的线程抢走啊!!所以就会出现交错出现的情况。