该例子运用前面
Java线程学习笔记(二)---多线程同步方法 中提到的同步关键字synchronized来进行线程的同步互斥。synchronized在类的方法声明中使用,可以保证在同一时刻只有一个线程能够进入该方法。
例子
本实例模拟银行ATM存取款机,分析一个用户往ATM机中存钱时,另一个用户在另一个地方从中取钱,存在操作前后不一致,程序采用线程同步与非同步方法解决的区别。
package Thread;
class Bank {
private double current_money=500.0d;//存入银行的钱数
public void putNonSynDeposit(double putMoney){//存钱没有采用同步机制
System.out.println("没有采用线程同步机制,钱为 "+ this.current_money + "; 存钱:"+putMoney);
System.out.println("等待中。。。。。。"); //存钱时先等待300毫秒
try {
Thread.sleep(300);//线程休眠
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("等待完毕进行存钱操作。。。。");
this.current_money=this.current_money+putMoney;
System.out.println("没有采用线程同步机制,当前钱数为" +this.current_money +"元");
}
public void takeNonSynWithdraw(double takeMoney){//取钱没有同步机制
System.out.println("没有采用线程同步机制,钱为" + this.current_money + ";取钱:" +takeMoney);
System.out.println("等待中。。。。");//取钱时先等待500毫秒
try {
Thread.sleep(500);//线程休眠
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("等待完毕进行取钱操作...");
this.current_money=this.current_money-takeMoney;
System.out.println("没有采用线程同步机制,当前钱数为" + this.current_money);
}
public synchronized void putSynDeposit(double putMoney){ //存钱有同步机制
System.out.println("采用线程同步机制,钱为" + this.current_money+";存钱: "+putMoney);
System.out.println("等待中。。。。");//存钱时先等待300毫秒
try {
Thread.sleep(300);//线程休眠
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("等待完毕进行存钱操作...");
this.current_money=this.current_money+putMoney;
System.out.println("采用线程同步机制,当前钱数为 "+ this.current_money+"元");
}
public synchronized void takeSynWithdraw(double takeMoney){//取钱有同步机制
System.out.println("采用线程同步机制,钱为"+ this.current_money+";取钱: "+takeMoney);
System.out.println("等待中。。。");//取钱时先等待500毫秒
try {
Thread.sleep(500);//线程休眠
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("等待完毕进行取钱操作...");
this.current_money=this.current_money-takeMoney;
System.out.println("采用线程同步机制,当前钱数为"+this.current_money+"元");
}
}
class AccessBank extends Thread{ //继承Thread类实现线程方法
private Bank bank=null; //待访问的账号对象
private String method=""; //访问账号的方法
public AccessBank(Bank account,String method){//构造方法进行初始化
this.bank=account;
this.method=method;
}
public void run(){ //实现Thread的方法
if(method.equals("putNonSynDeposit")){//不同参数调用不同的方法
bank.putNonSynDeposit(500.0);
}else if(method.equals("takeNonSynWithdraw")){
bank.takeNonSynWithdraw(200.0);
}else if(method.equals("putSynDeposit")){//存款有同步机制
bank.putSynDeposit(500.0);
}else if(method.equals("takeSynWithdraw")){
bank.takeSynWithdraw(200.0);
}
}
}
public class TextSynchronized {
public static void main(String[] args) {
Bank bank=new Bank(); //实例化对象
System.out.println("1.未采用同步机制时");
Thread putMoney=new AccessBank(bank,"putNonSynDeposit");//存钱没有采用同步机制
Thread takeMoney=new AccessBank(bank,"takeNonSynWithdraw");//取钱没有同步机制
putMoney.start();//启动线程
takeMoney.start();//启动线程
try {
//主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了主线程才能执行。
//(Waits for this thread to die.)
putMoney.join();//等待两线程结束
takeMoney.join();
} catch (Exception e) {
System.out.println("两线程运行出错: "+ e.getMessage());
}
System.out.println("======================");
bank=new Bank();
System.out.println("2.采用同步机制时。。。");
putMoney=new AccessBank(bank,"putSynDeposit");//存钱有同步机制
takeMoney=new AccessBank(bank,"takeSynWithdraw");//取钱有同步机制
putMoney.start();//启动线程
takeMoney.start();//启动线程
}
}
运行截图
代码解读
(1)代码首先展示了没有采用的同步机制的存取款机制,存取款的业务逻辑有错误。大家多试几次,就会发现未采用同步机制的存取款的代码执行顺序会变化,这对于实际的生产环境的业务是不允许的,所有要采用同步。
(2)Java中的线程同步可以通过synchronized关键字来实现,synchronized既可以修饰变量(必须是类对象),又可以修饰方法。被synchronized修饰的变量和方法同时只能有一个线程来操作,其他线程只能等待
(3)Bank类的putNonSynDeposit()方法实现存钱功能没有使用同步互斥技术,允许多个用户同时存钱或者取钱,这样就会出错。takeNonSynWithdraw()方法实现取钱功能没有使用同步功能互斥技术,这样也会出错。
(3)putSynDeposit()方法实现使用同步互斥存钱的功能,即在同一时刻只允许一个用户(线程)进行操作,当该线程进入同步方法后对象被锁,其他线程因为没有对象锁而不能访问该对象的该方法,只有当持有对象所得线程释放锁时,才有机会访问该方法,也就是当多个用户同时存钱是也都是顺序执行的,不会同时修改钱数。
(4)takeSynWithdraw()方法实现同步互斥取钱的功能,当多个用户同时取钱时是按顺序执行的,也不会同时修改钱数。