对于线程同步的理解

 
什么是线程同步?为什么需要线程同步?如何实现线程的同步?这是在学线程时,学生们经常会困惑的问题。我们从一个例子说明,这三个问题以及解决方案:
我们知道一般我们在银行开设一个帐户,银行会给我们一个存折和一个借记卡。我们可以分别拿卡或者存折来对一个帐户进行存取款操作。假设一个帐户上有余额5000元,而记录这个信息的数据库服务器经常不在你所做操作的银行,这样就存在先从服务器上获取余额数据,然后再进行存取款操作,第三步将存取款后的结果写入数据库(当然实际的操作可能有更复杂的业务逻辑)。我们如果同时使用存折和借记卡分别在不同的银行进行存取款操作,如果没有同步,那么将会出现错误。
 
一个银行帐户中有余额:5000
第一步:一个人使用银行存折在一个银行进行存款3000的操作:
       这个业务先要提取到余额5000,然后进行累加,变成8000。
第二步:另一个人使用借记卡在另一个银行的ATM机上取款2000的操作(假设是同时进行)。这个业务也先提取到余额5000,然后减去2000,变成3000。
第三步:ATM操作较快,将3000的余额写入数据库服务器。
第四步:第一步的存款操作这时候操作完毕,将8000写入数据库服务器。
 
很显然经过这个过程我们逻辑上的余额应该是6000才对。但是最后的结果却是8000。如果使用银行存折进行操作的数据8000先写入数据库,而ATM的操作后的数据5000再写入数据库的话就变成3000了。总之不会出现正确的结果,这就需要同步,也就是说如果一个人对这个帐户存款时不能让其他人再对这个账户进行存款或者取款操作,这样就不会出现上述的错误了。
       分析一下这个过程:
一个资源对象:一个银行账户
一个线程:使用存折进行存款操作
另一个线程:使用借记卡进行取款操作
 
使用代码模拟一下没有同步会出现错误的现象:
//定义一个账户类进行资源对象的定义
publicclass Account {
    privatelong accNum ;
    privatedouble money ;
    private String password ;
    public Account( long accNum, double money, String password) {
       super ();
       this . accNum = accNum;
       this . money = money;
       this . password = password;
    }
   
    // 存款方法
    publicvoid saveMoney( double money){
       System. out .println( " 存款操作开始 ..." );
       double tmoney= this . money ; // 拿到数据库上的帐户余额
       // 进行数据操作
       tmoney+=money;
       // 模拟操作延时
       try {
           Thread.sleep(200);
       } catch (InterruptedException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
       // 将修改的数据写入帐户
       this . money =tmoney;
       System. out .println( " 存款操作完毕,目前帐户余额: " + this . money );
    }
    // 取款方法
    publicvoid getMoney( double money){
       System. out .println( " 取款操作开始 ..." );
       double tmoney= this . money ; // 拿到数据库上的帐户余额
       // 进行数据操作
       tmoney-=money;
       // 模拟操作延时
       try {
           Thread.sleep(200);
       } catch (InterruptedException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
       // 将修改的数据写入帐户
       this . money =tmoney;
       System. out .println( " 取款操作完毕,目前帐户余额: " + this . money );
    }
}
//定义存款线程
publicclass SaveMoneyPerson extends Thread{
    private Account acc ;
 
    public SaveMoneyPerson(Account acc,String personName) {
       this .setName(personName);
       this . acc = acc;
    }
   
    // 执行存款
    publicvoid run(){
       // 存款 3000
       acc .saveMoney(3000);
    }
}
//定义取款线程
publicclass GetMoneyPerson extends Thread{
    private Account acc ;
 
    public GetMoneyPerson(Account acc,String personName) {
       this .setName(personName);
       this . acc = acc;
    }
   
    // 执行取款
    publicvoid run(){
       // 取款 3000
       acc .getMoney(2000);
    }
}
//执行
publicclass TestGetSaveMoney {
 
    publicstaticvoid main(String[] args) {
       Account acc= new Account(1001,5000, "123" );
       GetMoneyPerson gThread= new GetMoneyPerson(acc, "getMoneyPerson" );
       SaveMoneyPerson sThread= new SaveMoneyPerson(acc, "saveMoneyPerson" );
      
       gThread.start();
       sThread.start();
    }
}
//执行结果有两种:
1- 
取款操作开始 ...
存款操作开始 ...
取款操作完毕,目前帐户余额: 3000.0
存款操作完毕,目前帐户余额: 3000.0
 
2-
取款操作开始 ...
存款操作开始 ...
取款操作完毕,目前帐户余额: 8000.0
存款操作完毕,目前帐户余额: 8000.0
 
无论那一次都不对。
 
那么什么是线程同步?
线程同步:线程对于同一个资源对象使用的串行使用,而不是同时使用。这就是线程同步。
为什么需要线程同步?
很显然如果没有同步就会出现上述错误。
如果实现同步?
1-      通过在资源对象上定义同步方法就可以实现
 
publicclass Account {
    privatelong accNum ;
    privatedouble money ;
    private String password ;
    public Account( long accNum, double money, String password) {
       super ();
       this . accNum = accNum;
       this . money = money;
       this . password = password;
    }
   
    // 存款方法     使用关键字 synchronized 定义同步方法
    publicsynchronizedvoid saveMoney( double money){
       System. out .println( " 存款操作开始 ..." );
       double tmoney= this . money ; // 拿到数据库上的帐户余额
       // 进行数据操作
       tmoney+=money;
       // 模拟操作延时
       try {
           Thread.sleep(200);
       } catch (InterruptedException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
       // 将修改的数据写入帐户
       this . money =tmoney;
       System. out .println( " 存款操作完毕,目前帐户余额: " + this . money );
    }
    // 取款方法    使用关键字 synchronized 定义同步方法
    publicsynchronizedvoid getMoney( double money){
       System. out .println( " 取款操作开始 ..." );
       double tmoney= this . money ; // 拿到数据库上的帐户余额
       // 进行数据操作
       tmoney-=money;
       // 模拟操作延时
       try {
           Thread.sleep(200);
       } catch (InterruptedException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
       // 将修改的数据写入帐户
       this . money =tmoney;
       System. out .println( " 取款操作完毕,目前帐户余额: " + this . money );
    }
}
// 正确结果
取款操作开始 ...
取款操作完毕,目前帐户余额: 3000.0
存款操作开始 ...
存款操作完毕,目前帐户余额: 6000.0
 
2-      通过定义同步语句块实现
// 定义资源对象
publicclass Account {
    privatelong accNum ;
    privatedouble money ;
    private String password ;
    public Account( long accNum, double money, String password) {
       super ();
       this . accNum = accNum;
       this . money = money;
       this . password = password;
    }
    publiclong getAccNum() {
       return accNum ;
    }
    publicvoid setAccNum( long accNum) {
       this . accNum = accNum;
    }
    publicdouble getMoney() {
       return money ;
    }
    publicvoid setMoney( double money) {
       this . money = money;
    }
    public String getPassword() {
       return password ;
    }
    publicvoid setPassword(String password) {
       this . password = password;
    }
   
}
// 取款线程
 
publicclass GetMoneyPerson extends Thread{
    private Account acc ;
 
    public GetMoneyPerson(Account acc,String personName) {
       this .setName(personName);
       this . acc = acc;
    }
   
    // 执行取款
    publicvoid run(){
       synchronized ( acc ) {
           System. out . println ( " 取款操作开始 ..." );
           double tmoney = acc .getMoney(); // 拿到数据库上的帐户余额
           // 进行数据操作
           tmoney -= 2000;
           // 模拟操作延时
           try {
              Thread.sleep(200);
           } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
           }
           // 将修改的数据写入帐户
           acc .setMoney(tmoney);
           System. out .println( " 取款操作完毕,目前帐户余额: " + acc .getMoney());
       }
    }
}
// 存款线程
 
publicclass SaveMoneyPerson extends Thread{
    private Account acc ;
 
    public SaveMoneyPerson(Account acc,String personName) {
       this .setName(personName);
       this . acc = acc;
    }
   
    // 执行存款
    publicvoid run(){
       synchronized ( acc ){
           System. out .println( " 存款操作开始 ..." );
           double tmoney= acc .getMoney(); // 拿到数据库上的帐户余额
           // 进行数据操作
           tmoney+=3000;
           // 模拟操作延时
           try {
              Thread.sleep(200);
           } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
           }
           // 将修改的数据写入帐户
           acc .setMoney(tmoney);
           System. out .println( " 存款操作完毕,目前帐户余额: " + acc .getMoney());
       }
    }
}
// 执行
publicclass TestGetSaveMoney {
 
    publicstaticvoid main(String[] args) {
       Account acc= new Account(1001,5000, "123" );
       GetMoneyPerson gThread= new GetMoneyPerson(acc, "getMoneyPerson" );
       SaveMoneyPerson sThread= new SaveMoneyPerson(acc, "saveMoneyPerson" );
      
       gThread.start();
       sThread.start();
    }
 
}
// 结果
取款操作开始 ...
取款操作完毕,目前帐户余额: 3000.0
存款操作开始 ...
存款操作完毕,目前帐户余额: 6000.0
 
我们会发现一般定义同步方法是在资源对象中定义,而同步语句块是在线程中定义,这两种方法都可以实现同步。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值