synchronized關(guān)鍵字可以設(shè)置同步方法和同步塊,同步方法會對調(diào)用對象this上鎖,
所有線程進入前都需要獲取這個鎖,這里我拿一個錯誤樣例來做示范
public class Test {
public static void main(String[] args) {
Account account=new Account(100);
Person p1=new Person(account,80);
Person p2=new Person(account,90);
p1.start();
p2.start();
}
}
class Account{
int total;
public Account(int total) {
super();
this.total=total;
}
}
class Person extends Thread{
private int reduce;
private Account account;
public Person(Account account,int reduce) {
this.account=account;
this.reduce=reduce;
}
public synchronized void run() {
if (account.total-reduce<0) return ;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
account.total-=reduce;
System.out.println(Thread.currentThread().getName()+"取出"+reduce);
System.out.println(Thread.currentThread().getName()+"剩余"+account.total);
}
}
Thread-0取出80
Thread-0剩余-70
Thread-1取出90
Thread-1剩余-70
出現(xiàn)了負(fù)數(shù),很明顯沒鎖住,現(xiàn)在我們來分析下原因。
有1個account對象,兩個person對象p1和p2,p1和p2爭搶資源account
我一開始的設(shè)想是,當(dāng)p1進入方法時,p1對象上鎖,account作為p1的成員也被上鎖,此時p2進入就需要等到p1釋放鎖,由此達(dá)到我們的目標(biāo)。
問題在于:java規(guī)定每個對象都有一個監(jiān)視器,使用時查看上鎖了沒,上面的代碼中雖然對person對象上了鎖,同時account作為對象也有監(jiān)視器,account雖然作為person的成員,但account的監(jiān)視器是單獨的,不受p1的鎖影響?,F(xiàn)在我們再來場景復(fù)原一下,p1先進入方法,對p1上鎖,計算100-80=20,同時p2進入方法,查看account的監(jiān)視器,沒有上鎖,計算20-90=-70,這就產(chǎn)生了錯誤。
解決方法就是使用同步塊給account上鎖
public void run() {
synchronized(account) {
if (account.total-reduce<0) return ;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
account.total-=reduce;
System.out.println(Thread.currentThread().getName()+"取出"+reduce);
System.out.println(Thread.currentThread().getName()+"剩余"+account.total);
}
}
這個問題給我們的教訓(xùn)在于,每個對象都有單獨的監(jiān)視器,要選取正確的爭奪對象上鎖,基于此同步塊不僅能縮小鎖的粒度還能選取正確的對象上鎖。