Java当中,对于同步,一般而言,在程序中需完成两个操作:
- 把竞争访问的资源标示为private;
- 同步那些访问资源的代码,使用synchronize关键字来修饰方法或者代码块,当synchronize方法执行完成或发生异常的时候,会自动的释放锁
下面用这个例子来讲解:
某银行账号上有500元的现金,一个人拿着正卡去柜台取钱,而另一个人则拿着副卡器ATM机上去取钱,各自取400元钱。
要求:不能出现资源竞争,如同时取出400元,或者取的钱大于500元等;
package hellojava;
/**
* 某账号上有500块钱,一个人拿着存折去柜台取,另一个人拿着银行卡去ATM机上取
* 各自想取400块钱
*/
public class BankDemo {
public static void main(String[] args) {
Bank b = new Bank();
BankThread bt1= new BankThread(b);
bt1.start();
BankThread bt2 = new BankThread(b);
bt2.start();
}
}
class BankThread extends Thread{
//这样就实现了封装,不用到处new
private Bank bank=null;
public BankThread(Bank bank) {
this.bank=bank;
}
@Override
public void run() {
System.out.println("取钱:"+bank.getMoney(400));
}
}
/**
* 这种情况是不安全的,两个线程同时会去访问,
* 造成的后果就是各自为营,互不干涉,这不是我们想要的结果
* Bank才是正确的
*
*/
class Bank1{
private int money = 500;
//取钱的方法,返回取钱的数目
public int getMoney(int number){
if(number <0){
return -1;
}else if (money<0) {
return -2;
}else if (number-money>0) {
return -3;
}else {
//隔了这么久之后,成功的拿到钱
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
money-=number;
System.out.println("卡上余额:"+money);
}
return number;
}
}
/**
* 当一个线程去调用一个同步方法的时候,这个线程就获得当前对象的锁
* 而其他线程去调用这个同步方法的时候,就只能去等待,因为无法获得这个对象的锁
* 只有等第一个线程释放了对象的锁,才可以去访问,加上synchronized
*
* 或者是在方法体内加上synchronized(){}代码块,括号当中是要锁的对象,比如this,就代表当前对象
*
*/
class Bank{
private int money = 500;
//取钱的方法,返回取钱的数目
public synchronized int getMoney(int number){
if(number <0){
return -1;
}else if (money<0) {
return -2;
}else if (number-money>0) {
return -3;
}else {
//隔了这么久之后,成功的拿到钱
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
money-=number;
System.out.println("卡上余额:"+money);
}
return number;
}
}