Synchronized">Synchronized
751
2025-04-04
原文:http://www.cnblogs.com/benshan/p/3551987.html
最近在做一個監(jiān)控系統(tǒng),該系統(tǒng)主要包括對數(shù)據(jù)實(shí)時分析和存儲兩個部分,由于并發(fā)量比較高,所以不可避免的使用到了一些并發(fā)的知識。為了實(shí)現(xiàn)這些要求,后臺使用一個隊(duì)列作為緩存,對于請求只管往緩存里寫數(shù)據(jù)。同時啟動一個線程監(jiān)聽該隊(duì)列,檢測到數(shù)據(jù),立即請求調(diào)度線程,對數(shù)據(jù)進(jìn)行處理。 具體的使用方案就是使用同步保證數(shù)據(jù)的正常,使用線程池提高效率。
同步的實(shí)現(xiàn)當(dāng)然是采用鎖了,java中使用鎖的兩個基本工具是 Synchronized 和 Lock。
一直很喜歡Synchronized,因?yàn)槭褂盟芊奖恪1热纾枰獙σ粋€方法進(jìn)行同步,那么只需在方法的簽名添加一個synchronized關(guān)鍵字。
// 未同步的方法
public void test() {}
// 同步的方法
pubilc synchronized void test() {}
synchronized 也可以用在一個代碼塊上,看
public void test() {
synchronized(obj) {
System.out.println("===");
}
}
synchronized 用在方法和代碼塊上有什么區(qū)別呢?
synchronized 用在方法簽名上(以test為例),當(dāng)某個線程調(diào)用此方法時,會獲取該實(shí)例的對象鎖,方法未結(jié)束之前,其他線程只能去等待。當(dāng)這個方法執(zhí)行完時,才會釋放對象鎖。其他線程才有機(jī)會去搶占這把鎖,去執(zhí)行方法test,但是發(fā)生這一切的基礎(chǔ)應(yīng)當(dāng)是所有線程使用的同一個對象實(shí)例,才能實(shí)現(xiàn)互斥的現(xiàn)象。否則synchronized關(guān)鍵字將失去意義。
( 但是如果該方法為類方法,即其修飾符為static,那么synchronized 意味著某個調(diào)用此方法的線程當(dāng)前會擁有該類的鎖,只要該線程持續(xù)在當(dāng)前方法內(nèi)運(yùn)行,其他線程依然無法獲得方法的使用權(quán)!)
synchronized 用在代碼塊的使用方式:synchronized(obj){//todo code here}
當(dāng)線程運(yùn)行到該代碼塊內(nèi),就會擁有obj對象的對象鎖,如果多個線程共享同一個Object對象,那么此時就會形成互斥!特別的,當(dāng)obj == this時,表示當(dāng)前調(diào)用該方法的實(shí)例對象。即
public void test() {
...
synchronized(this) {
// todo your code
}
...
}
此時,其效果等同于
public synchronized void test() {
// todo?your code
}
使用synchronized代碼塊,可以只對需要同步的代碼進(jìn)行同步,這樣可以大大的提高效率。
小結(jié):
使用synchronized 代碼塊相比方法有兩點(diǎn)優(yōu)勢:
1、可以只對需要同步的使用
2、與wait()/notify()/nitifyAll()一起使用時,比較方便
----------------------------------------------------------------------------------------------------------------------------------------------------------
wait() 與notify()/notifyAll()
這三個方法都是Object的方法,并不是線程的方法!
wait():釋放占有的對象鎖,線程進(jìn)入等待池,釋放cpu,而其他正在等待的線程即可搶占此鎖,獲得鎖的線程即可運(yùn)行程序。而sleep()不同的是,線程調(diào)用此方法后,會休眠一段時間,休眠期間,會暫時釋放cpu,但并不釋放對象鎖。也就是說,在休眠期間,其他線程依然無法進(jìn)入此代碼內(nèi)部。休眠結(jié)束,線程重新獲得cpu,執(zhí)行代碼。 wait()和sleep()最大的不同在于wait()會釋放對象鎖,而sleep()不會!
notify(): 該方法會喚醒因?yàn)檎{(diào)用對象的wait()而等待的線程,其實(shí)就是 對對象鎖的喚醒,從而使得wait()的線程可以有機(jī)會獲取對象鎖。調(diào)用notify()后,并不會立即釋放鎖,而是繼續(xù)執(zhí)行當(dāng)前代碼,直到synchronized中的代碼全部執(zhí)行完畢,才會釋放對象鎖。JVM則會在等待的線程中調(diào)度一個線程去獲得對象鎖,執(zhí)行代碼。需要注意的是, wait()和notify()必須在synchronized代碼塊中調(diào)用。
notifyAll()則是喚醒所有等待的線程。
為了說明這一點(diǎn),舉例如下:
兩個線程依次打印"A""B",總共打印10次。
public? class? Consumer? implements? Runnable {
@Override
public? synchronized? void? run() {
//? TODO? Auto-generated method stub
int? count = 10;
while (count > 0) {
synchronized? (Test.? obj ) {
System.? out .print(? "B" );
count --;
Test.? obj .notify();? // 主動釋放對象鎖
try? {
Test.? obj .wait();
}? catch? (InterruptedException e) {
//? TODO? Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public? class? Produce? implements? Runnable {
@Override
public? void? run() {
//? TODO? Auto-generated method stub
int? count = 10;
while (count > 0) {
synchronized? (Test.? obj ) {
//System.out.print("count = " + count);
System.? out .print(? "A" );
count --;
Test.? obj .notify();
try? {
Test.? obj .wait();
}? catch? (InterruptedException e) {
//? TODO? Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
測試類如下:
public? class? Test {
public? static? final? Object? obj? =? new? Object();
public? static? void? main(String[] args) {
new? Thread(? new? Produce()).start();
new? Thread(? new? Consumer()).start();
}
}
這里使用static obj作為鎖的對象,當(dāng)線程Produce啟動時(假如Produce首先獲得鎖,則Consumer會等待),打印“A”后,會先主動釋放鎖,然后阻塞自己。Consumer獲得對象鎖,打印“B”,然后釋放鎖,阻塞自己,那么Produce又會獲得鎖,然后...一直循環(huán)下去,直到count = 0.這樣,使用Synchronized和wait()以及notify()就可以達(dá)到線程同步的目的。
----------------------------------------------------------------------------------------------------------------------------------------------------------
除了wait()和notify()協(xié)作完成線程同步之外,使用Lock也可以完成同樣的目的。
ReentrantLock 與synchronized有相同的并發(fā)性和內(nèi)存語義,還包含了中斷鎖等候和定時鎖等候,意味著線程A如果先獲得了對象obj的鎖,那么線程B可以在等待指定時間內(nèi)依然無法獲取鎖,那么就會自動放棄該鎖。
但是由于synchronized是在JVM層面實(shí)現(xiàn)的,因此系統(tǒng)可以監(jiān)控鎖的釋放與否,而ReentrantLock使用代碼實(shí)現(xiàn)的,系統(tǒng)無法自動釋放鎖,需要在代碼中finally子句中顯式釋放鎖lock.unlock();
同樣的例子,使用lock 如何實(shí)現(xiàn)呢?
public? class? Consumer? implements? Runnable {
private? Lock? lock ;
public? Consumer(Lock lock) {
this .? lock? = lock;
}
@Override
public? void? run() {
//? TODO? Auto-generated method stub
int? count = 10;
while ( count > 0 ) {
try? {
lock .lock();
count --;
System.? out .print(? "B" );
}? finally? {
lock .unlock(); //主動釋放鎖
try? {
Thread.?sleep(91L);
}? catch? (InterruptedException e) {
//? TODO? Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public? class? Producer? implements? Runnable{
private? Lock? lock ;
public? Producer(Lock lock) {
this .? lock? = lock;
}
@Override
public? void? run() {
//? TODO? Auto-generated method stub
int? count = 10;
while? (count > 0) {
try? {
lock .lock();
count --;
System.? out .print(? "A" );
}? finally? {
lock .unlock();
try? {
Thread.?sleep(90L);
}? catch? (InterruptedException e) {
//? TODO? Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
調(diào)用代碼:
public? class? Test {
public? static? void? main(String[] args) {
Lock lock =? new? ReentrantLock();
Consumer consumer =? new? Consumer(lock);
Producer?producer?=? new? Producer(lock);
new? Thread(consumer).start();
new? Thread(?producer).start();
}
}
使用建議:
在并發(fā)量比較小的情況下,使用synchronized是個不錯的選擇,但是在并發(fā)量比較高的情況下,其性能下降很嚴(yán)重,此時ReentrantLock是個不錯的方案。
任務(wù)調(diào)度
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。