黑马程序员技术交流社区

标题: 为什么每次执行的结果不一样 [打印本页]

作者: 田向向    时间: 2012-6-24 00:26
标题: 为什么每次执行的结果不一样
本帖最后由 田向向 于 2012-6-24 22:00 编辑

package txx;
public class Account {  
            private int balance;  
         
    public Account(int balance) {  
               this.balance = balance;  
            }  
        
            public int getBalance() {  
                return balance;  
            }  
        
           public void add(int num) {  
                balance = balance + num;  
            }  
         
            public void withdraw(int num) {  
                balance = balance - num;  
            }  
         
            public static void main(String[] args) throws InterruptedException {  
               Account account = new Account(1000);  
                Thread a = new Thread(new AddThread(account, 20), "add");  
               Thread b = new Thread(new WithdrawThread(account, 20), "withdraw");  
              a.start();  
                b.start();  
       a.join();  
               b.join();  
                System.out.println(account.getBalance());  
          }  
         
            static class AddThread implements Runnable {  
               Account account;  
                int     amount;  
        
               public AddThread(Account account, int amount) {  
                   this.account = account;  
                    this.amount = amount;  
               }  
         
                public void run() {  
                  for (int i = 0; i < 200000; i++) {  
                        account.add(amount);  
                    }  
      }  
            }  
         
           static class WithdrawThread implements Runnable {  
                Account account;  
                int     amount;  
         
                public WithdrawThread(Account account, int amount) {  
                    this.account = account;  
                    this.amount = amount;  
               }  
         
                public void run() {  
                   for (int i = 0; i < 100000; i++) {  
                       account.withdraw(amount);  
                   }  
                }  
           }  
        }  
第一次执行结果是2244600,第二次是3120220,第三次是2898720,我是菜鸟,求解释
作者: 周兴中    时间: 2012-6-24 03:28
本帖最后由 周兴中 于 2012-6-24 03:34 编辑

由于你操作共享数据时没有进行同步处理,使得balance在被进行加减操作时,出现被多加或者多减的情况,当运行次数足够多时,很容易出现不一样的结果.
例如:
balance = balance + num;  假设此时 balance为1000,num为20  
在balance还没有被赋值时, 此时balance仍为1000,被b线程抢走了执行权.
执行了这句 balance = balance - num;   此时balance为980
然后当a线程再次拿到执行权时,它仍认为balance为1000,赋值后结果为1020 ,结果错误.

例2

public class MyThread extends Thread
{
    public static int n = 0;

    public void run()
    {
        int m = n;
        yield();
        m++;
        n = m;
    }
    public static void main(String[] args) throws Exception
    {
        MyThread myThread = new MyThread ();
        Thread threads[] = new Thread[100];
        for (int i = 0; i < threads.length; i++)
            threads = new Thread(myThread);
        for (int i = 0; i < threads.length; i++)
            threads.start();
        for (int i = 0; i < threads.length; i++)
            threads.join();
        System.out.println("n = " + MyThread.n);
    }
}
由于先调用了thread1的start方法,因此,thread1的run方法一般会先运行。当thread1的run方法运行到第一行(int m = n;)时,将n的值赋给m。当执行到第二行的yield方法后,thread1就会暂时停止执行,而当thread1暂停时,thread2获得了CPU资源后开始运行(之前thread2一直处于就绪状态),当thread2执行到第一行(int m = n;)时,由于thread1在执行到yield时n仍然是0,因此,thread2中的m获得的值也是0。这样就造成了thread1和thread2的m获得的都是0。在它们执行完yield方法后,都是从0开始加1,因此,无论谁先执行完,最后n的值都是1,只是这个n被thread1和thread2各赋了一遍值。这个过程如下图如示:


所以,当对共享数据进行写操作时,要进行数据同步,
数据同步就是指在同一时间,只能由一个线程来访问被同步的类变量,当前线程访问完这些变量后,其他线程才能继续访问。

你的代码要想得到正确的结果,可以简单的在add和 withdraw 方法前加 synchronized修饰. 即可同步

作者: 彭盼    时间: 2012-6-24 17:08
两个线程没有同步而已,我修改了你的程序:
public class Account {  
    private int balance;  
    private Object obj;

public Account(int balance) {  
       this.balance = balance;  
       obj=new Object();//让对象初始化时获得一把锁
    }  

    public int getBalance() {  
        return balance;  
    }  
    public Object getObj(){
            return obj;
    }

   public void add(int num) {  
        balance = balance + num;  
    }  

    public void withdraw(int num) {  
        balance = balance - num;  
    }  
  
    public static void main(String[] args) throws InterruptedException {  
       Account account = new Account(1000);  
        Thread a = new Thread(new AddThread(account, 20), "add");  
       Thread b = new Thread(new WithdrawThread(account, 20), "withdraw");  
      a.start();  
        b.start();  
a.join();  
       b.join();  
        System.out.println(account.getBalance());  
  }  

    static class AddThread implements Runnable {  
       Account account;  
        int     amount;  

       public AddThread(Account account, int amount) {  
           this.account = account;  
            this.amount = amount;  
       }  
      
        public void run() {
                synchronized (account.obj) {//加入同步代码块,使线程安全
                                 for (int i = 0; i < 200000; i++) {  
                account.add(amount);  
            }  
                        }
         
}  
    }  
  
   static class WithdrawThread implements Runnable {  
        Account account;  
        int     amount;  

        public WithdrawThread(Account account, int amount) {  
            this.account = account;  
            this.amount = amount;  
       }  
  
        public void run() {  
                synchronized (account.obj) {//加入同步代码块,使线程安全
                                 for (int i = 0; i < 100000; i++) {  
               account.withdraw(amount);  
           }
                        }
        }  
   }  
}  




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2