在介绍synchronized方法之前,我们先来分析下面的代码
/*
* 线程不安全,取钱问题
* */
public class UnsafeTest02 {
public static void main(String[] args) {
//账户
Account account=new Account(100, "存款");
Drawing myDrawing =new Drawing(account, 50, "我");
Drawing HisDrawing =new Drawing(account, 70, "爸爸");
myDrawing.start();
HisDrawing.start();
}
}
class Account{
int money;
String nameString;
public Account(int money, String nameString) {
super();
this.money = money;
this.nameString = nameString;
}
}
class Drawing extends Thread{
Account account;
int drawingMoney;
public Drawing(Account account, int drawingMoney,String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
// TODO Auto-generated method stub
if(account.money<drawingMoney) {
System.out.println("余额不足");
return;
}
//模拟网络延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
account.money-=drawingMoney;
System.out.println(this.getName()+"--->总共取了"+drawingMoney+"--->账户余额为"+account.money);
}
}
我--->总共取了50--->账户余额为50
爸爸--->总共取了70--->账户余额为-20
我和爸爸共用一个银行账户,账户里面有100元。有一天,我和爸爸在对方不知情的情况下同时取钱。我想取50元,爸爸想取70元。我和爸爸同时都操作银行账户取钱,可能我在柜台取钱,爸爸用银行卡在自动取款机取钱。我和爸爸同时发起取钱线程,但是我这边操作更快,先取得了对账户的控制权,先对账户进行取50元,但是由于线程延时Thread.Sleep(100),我还未来得及修改账户中还剩下的余额,爸爸那头也在操作取70元。所有出现了银行余额为-20。这类情况被称为线程同步不安全。如何解决这样的情况?我们引入了synchronized修饰方法。
下面代码是操作同步代码块
public class SynTest02 {
public static void main(String[] args) {
//账户
Account account=new Account(100, "存款");
SafeDrawing myDrawing =new SafeDrawing(account, 50, "我");
SafeDrawing HisDrawing =new SafeDrawing(account, 70, "爸爸");
myDrawing.start();
HisDrawing.start();
}
}
class Account{
int money;
String nameString;
public Account(int money, String nameString) {
super();
this.money = money;
this.nameString = nameString;
}
}
class SafeDrawing extends Thread{
Account account;
int drawingMoney;
public SafeDrawing(Account account, int drawingMoney,String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
// TODO Auto-generated method stub
//使用account为同步监视器,任何线程在进入下面的同步代码块之前
//必须先获得account账户的锁,-----其他线程无法获得锁,无法修改Account
synchronized (account) {
if(account.money<drawingMoney) {
System.out.println(this.getName()+"---余额不足");
return;
}
//模拟网络延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//修改余额
account.money-=drawingMoney;
System.out.println(this.getName()+"--->总共取了"+drawingMoney+"--->账户余额为"+account.money);
}
}
//同步代码块结束,释放锁
}
我--->总共取了50--->账户余额为50
爸爸--->余额不足
引入synchronized方法可以解决线程不同步的问题。
上述代码是操作同步代码块,如果sysnchronized直接修饰方法,同步方法如下
public class SynTest02 {
public static void main(String[] args) {
//账户
Account1 account=new Account1(100, "存款");
SafeDrawing myDrawing =new SafeDrawing(account, 50, "我");
SafeDrawing HisDrawing =new SafeDrawing(account, 70, "爸爸");
myDrawing.start();
HisDrawing.start();
}
}
class Account1{
//账户总共的钱
int money;
//账户名称
String nameString;
//要取的钱
int drawingTotal;
public Account1(int money, String nameString) {
super();
this.money = money;
this.nameString = nameString;
}
//同步方法
public synchronized void Drawing(int drawingMoney) {
if(money<drawingMoney) {
System.out.println(Thread.currentThread().getName()+"---取钱失败,余额不足");
return;
}
//模拟网络延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
money-=drawingMoney;
System.out.println(Thread.currentThread().getName()+"--->总共取了"+drawingMoney+"--->账户余额为"+money);
}
}
class SafeDrawing extends Thread{
Account1 account;
int drawingMoney;
public SafeDrawing(Account1 account, int drawingMoney,String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
// TODO Auto-generated method stub
account.Drawing(drawingMoney);
}
}
我--->总共取了50--->账户余额为50
爸爸---取钱失败,余额不足
关于Synchoronized的使用,如下表