Java: 简单模拟多线程访问同样变量导致的问题

来源:互联网 发布:金融软件有哪些 编辑:程序博客网 时间:2024/05/17 21:54
简单模拟多线程访问同样变量导致的问题,需要使用Transaction控制,或加锁。
package practice;

public class RyanAndMonicaJob implements Runnable{
    
private BankAccount account = new BankAccount();
    
public static void main(String[] args){
        RyanAndMonicaJob runner 
= new RyanAndMonicaJob();
        Thread one 
= new Thread(runner);
        Thread two 
= new Thread(runner);
        one.setName(
"Ryan");
        two.setName(
"Monica");
        one.start();
        two.start();
    }

    
    
public void run(){
        
for(int i=0; i< 10; i++){
            withdraw(
30);
        }

        
if(account.getBalance()<0){
            System.out.println(
"the account is overdrawed!");
        }

    }

    
private void withdraw(int amount){
        
if(account.getBalance()>amount){
            
try{
                System.out.println(Thread.currentThread().getName() 
+" is going to sleep.");
                Thread.sleep(
500);
            }
catch(Exception e){
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() 
+ " woke up");
            account.withdraw(amount);
            System.out.println(
"after " + Thread.currentThread().getName() + " withdraw, the account is: " + account.getBalance());
        }
else{
            System.out.println(
"sorry " + Thread.currentThread().getName() + " the money is : " + account.getBalance());
        }

    }

}


package practice;

public class BankAccount {
    
int balance = 200;
    
public int getBalance(){
        
return balance;
    }

    
public void withdraw(){
        balance 
= balance - 150;
    }

}

修正其中的问题方法一:把其中的方法作为一个Transaction

package practice;

public class RyanAndMonicaJob implements Runnable{
    
private BankAccount account = new BankAccount();
    
public static void main(String[] args){
        RyanAndMonicaJob runner 
= new RyanAndMonicaJob();
        Thread one 
= new Thread(runner);
        Thread two 
= new Thread(runner);
        one.setName(
"Ryan");
        two.setName(
"Monica");
        one.start();
        two.start();
    }

    
    
public void run(){
        
for(int i=0; i< 10; i++){
            withdraw(
30);
        }

        
if(account.getBalance()<0){
            System.out.println(
"the account is overdrawed!");
        }

    }

    
private synchronized void withdraw(int amount){
        
if(account.getBalance()>amount){
            
try{
                System.out.println(Thread.currentThread().getName() 
+" is going to sleep.");
                Thread.sleep(
500);
            }
catch(Exception e){
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() 
+ " woke up");
            account.withdraw(amount);
            System.out.println(
"after " + Thread.currentThread().getName() + " withdraw, the account is: " + account.getBalance());
        }
else{
            System.out.println(
"sorry " + Thread.currentThread().getName() + " the money is : " + account.getBalance());
        }

    }

}

其中只增加了一个关键字:synchronized 同步方法非常简单,这样方法就作为了原子来执行。

但是,如果只有这个方法访问account,那么不会出现问题,如果还有方法需要访问account,那么需要使用Object key 才有效了,否则即便这个方法封锁了自己,仍然可能获得错误的account而执行错误的操作。

如下的例子导致了“Lost Update”
package practice;

public class TestSync implements Runnable{
    
private int balance;
    
    
public void run(){
        
for(int i=0;i<5000;i++){
            increaseBalance();
        }

    }

    
    
private void increaseBalance(){
        System.out.println(Thread.currentThread().getName() 
+ ++balance);
    }

}
package practice;

public class TestSyncTest {
    
public static void main(String[] args){
        TestSync runner 
= new TestSync();
        Thread one 
= new Thread(runner);
        Thread two 
= new Thread(runner);
        one.setName(
"A");
        two.setName(
"B");
        one.start();
        two.start();
    }

}


在执行过程中是很有出错概率的。解决方法还是一样的:在该increaseBalance()上加上synchronized关键字。
出现的适用范围也是一样,只有歌操作balance的的方法是有效的,但是如果还有其它线程用不同方法操作balance就会遇到问题。
原创粉丝点击