这里来记录一下同步方法和同步块的一些学习笔记
在跟着“狂神说”学习Java线程的时候,学到同步方法和同步块的时候发现老师讲的可能没有特别明白,我这里根据自己的理解,记录一下自己的理解。
先放上取钱案例
public class drawing {
public static void main(String[] args) {
Account account = new Account(100,"银行卡");
Draw p1 = new Draw(account,80,"p1");
Draw p2=new Draw(account,70,"p2");
p1.start();
p2.start();
}
}
class Account{
int money;
String name;//账户名称
public Account(int money,String name) {
this.name = name;
this.money=money;
}
}
class Draw extends Thread{
Account account;
int drawingMoney; //单次取钱
int nowMoney; //手里的钱
public Draw(Account account,int drawingMoney,String name){
super(name); //调用线程构造函数,给线程命名
this.account=account;
this.drawingMoney=drawingMoney;
}
@Override
public void run() {
if(account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"余额不足");
return;
}
//线程休眠,模拟网络延迟,让两个线程都运行到这里停止一秒钟,否则观察不到并发问题
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money -= drawingMoney;
nowMoney+=drawingMoney;
System.out.println(account.name+"余额为:"+account.money);
System.out.println(this.getName()+"手里的钱:"+nowMoney);
/*
银行卡余额为:30
p2手里的钱:70
银行卡余额为:-50
p1手里的钱:80
*/
}
}
很明显,这里是两个线程访问同一个account 的时候没有加锁,让其同时认为余额是100,对余额减了两次。
下面使用synchronized关键词,对方法添加同步
@Override
public synchronized void run() {
if(account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"余额不足");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money -= drawingMoney;
nowMoney+=drawingMoney;
System.out.println(account.name+"余额为:"+account.money);
System.out.println(this.getName()+"手里的钱:"+nowMoney);
/*
银行卡余额为:-50
p2手里的钱:70
银行卡余额为:-50
p1手里的钱:80
*/
}
结果是仍然没有变化。
原因:因为使用同步方法,相当于是对this加锁,在这里就是Draw所创建出来的对象。而p1,p2分别是各自创建了对象,所以这里的this对两个对象是不一样的,本来this也不会发生并发问题,所以对其加锁并没有什么用。
下面是同步块的方法
@Override
public void run() {
synchronized(account){
if(account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"余额不足");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money -= drawingMoney;
nowMoney+=drawingMoney;
System.out.println(account.name+"余额为:"+account.money);
System.out.println(this.getName()+"手里的钱:"+nowMoney);
}
/*
银行卡余额为:20
p1手里的钱:80
p2余额不足
*/
}
可以看到这里很好的处理的并发的问题
原因:这里的加锁对象是account,可以看出p1,p2是对同一个account对象访问的,所以对account加锁可以解决并发问题。