synchronized(這里的對(duì)象你看成一道門(mén)) {
成都創(chuàng)新互聯(lián)總部坐落于成都市區(qū),致力網(wǎng)站建設(shè)服務(wù)有網(wǎng)站建設(shè)、成都網(wǎng)站制作、網(wǎng)絡(luò)營(yíng)銷(xiāo)策劃、網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站維護(hù)、公眾號(hào)搭建、微信小程序定制開(kāi)發(fā)、軟件開(kāi)發(fā)等為企業(yè)提供一整套的信息化建設(shè)解決方案。創(chuàng)造真正意義上的網(wǎng)站建設(shè),為互聯(lián)網(wǎng)品牌在互動(dòng)行銷(xiāo)領(lǐng)域創(chuàng)造價(jià)值而不懈努力!
這里是一個(gè)人進(jìn)來(lái)了,把門(mén)反鎖了
需要同步操作的代碼
這里是里面的人事情做完了,出去了,門(mén)開(kāi)著其他人可以進(jìn)了
}
至于怎么鎖的,這是java和jvm的規(guī)定和實(shí)現(xiàn)細(xì)節(jié),作為普通程序員沒(méi)必要深入那么多。
多線程的同步依靠的是對(duì)象鎖機(jī)制 synchronized關(guān)鍵字的背后就是利用了封鎖來(lái)實(shí)現(xiàn)對(duì)共享資源的互斥訪問(wèn)
下面以一個(gè)簡(jiǎn)單的實(shí)例來(lái)進(jìn)行對(duì)比分析 實(shí)例要完成的工作非常簡(jiǎn)單 就是創(chuàng)建 個(gè)線程 每個(gè)線程都打印從 到 這 個(gè)數(shù)字 我們希望線程之間不會(huì)出現(xiàn)交叉亂序打印 而是順序地打印
先來(lái)看第一段代碼 這里我們?cè)趓un()方法中加入了synchronized關(guān)鍵字 希望能對(duì)run方法進(jìn)行互斥訪問(wèn) 但結(jié)果并不如我們希望那樣 這是因?yàn)檫@里synchronized鎖住的是this對(duì)象 即當(dāng)前運(yùn)行線程對(duì)象本身 代碼中創(chuàng)建了 個(gè)線程 而每個(gè)線程都持有this對(duì)象的對(duì)象鎖 這不能實(shí)現(xiàn)線程的同步
代碼??? package vista;??? class MyThread implements java lang Runnable {???? private int threadId;
public MyThread(int id) {????? this threadId = id;???? }
@Override???? public synchronized void run() {????? for (int i = ; i ; ++i) {?????? System out println( Thread ID: + this threadId + : + i);????? }???? }??? }
public class ThreadDemo {???? /**????? * @param args????? * @throws InterruptedException????? */???? public static void main(String[] args) throws InterruptedException {????? for (int i = ; i ; ++i) {?????? new Thread(new MyThread(i)) start();?????? Thread sleep( );????? }???? }??? }
從上述代碼段可以得知 要想實(shí)現(xiàn)線程的同步 則這些線程必須去競(jìng)爭(zhēng)一個(gè)唯一的共享的對(duì)象鎖
基于這種思想 我們將第一段代碼修改如下所示 在創(chuàng)建啟動(dòng)線程之前 先創(chuàng)建一個(gè)線程之間競(jìng)爭(zhēng)使用的Object對(duì)象 然后將這個(gè)Object對(duì)象的引用傳遞給每一個(gè)線程對(duì)象的lock成員變量 這樣一來(lái) 每個(gè)線程的lock成員都指向同一個(gè)Object對(duì)象 我們?cè)趓un方法中 對(duì)lock對(duì)象使用synchronzied塊進(jìn)行局部封鎖 這樣就可以讓線程去競(jìng)爭(zhēng)這個(gè)唯一的共享的對(duì)象鎖 從而實(shí)現(xiàn)同步
代碼??? package vista;
class MyThread implements java lang Runnable {???? private int threadId;???? private Object lock;
public MyThread(int id Object obj) {????? this threadId = id;????? this lock = obj;???? }
@Override???? public void run() {????? synchronized (lock) {?????? for (int i = ; i ; ++i) {??????? System out println( Thread ID: + this threadId + : + i);?????? }????? }???? }??? }
public class ThreadDemo {???? /**????? * @param args????? * @throws InterruptedException????? */???? public static void main(String[] args) throws InterruptedException {????? Object obj = new Object();????? for (int i = ; i ; ++i) {?????? new Thread(new MyThread(i obj)) start();?????? Thread sleep( );????? }???? }??? }
從第二段代碼可知 同步的關(guān)鍵是多個(gè)線程對(duì)象競(jìng)爭(zhēng)同一個(gè)共享資源即可 上面的代碼中是通過(guò)外部創(chuàng)建共享資源 然后傳遞到線程中來(lái)實(shí)現(xiàn) 我們也可以利用類(lèi)成員變量被所有類(lèi)的實(shí)例所共享這一特性 因此可以將lock用靜態(tài)成員對(duì)象來(lái)實(shí)現(xiàn) 代碼如下所示
代碼??? package vista;
class MyThread implements java lang Runnable {???? private int threadId;???? private static Object lock = new Object();
public MyThread(int id) {????? this threadId = id;???? }
@Override???? public void run() {????? synchronized (lock) {?????? for (int i = ; i ; ++i) {??????? System out println( Thread ID: + this threadId + : + i);?????? }????? }???? }??? }
public class ThreadDemo {???? /**????? * @param args????? * @throws InterruptedException????? */???? public static void main(String[] args) throws InterruptedException {????? for (int i = ; i ; ++i) {?????? new Thread(new MyThread(i)) start();?????? Thread sleep( );????? }???? }??? }
再來(lái)看第一段代碼 實(shí)例方法中加入sychronized關(guān)鍵字封鎖的是this對(duì)象本身 而在靜態(tài)方法中加入sychronized關(guān)鍵字封鎖的就是類(lèi)本身 靜態(tài)方法是所有類(lèi)實(shí)例對(duì)象所共享的 因此線程對(duì)象在訪問(wèn)此靜態(tài)方法時(shí)是互斥訪問(wèn)的 從而可以實(shí)現(xiàn)線程的同步 代碼如下所示
代碼??? package vista;
class MyThread implements java lang Runnable {???? private int threadId;
public MyThread(int id) {????? this threadId = id;???? }
@Override???? public void run() {????? taskHandler(this threadId);???? }
private static synchronized void taskHandler(int threadId) {????? for (int i = ; i ; ++i) {?????? System out println( Thread ID: + threadId + : + i);????? }???? }??? }
lishixinzhi/Article/program/Java/gj/201311/27441
Java的同步可以用synchronized關(guān)鍵字來(lái)實(shí)現(xiàn)。\x0d\x0a \x0d\x0asychronized可以同步代碼,需要綁定一個(gè)對(duì)象,如synchronized(obj){}\x0d\x0a也可以同步一個(gè)方法,是對(duì)方法進(jìn)行線程同步。如public void synchronized methodA(){}
public static Object lock=new Object(); // 創(chuàng)建一個(gè)對(duì)象, 他是一個(gè)靜態(tài)變量。
public static DataBaseDS getInstance(){
// 這里是判斷這個(gè)對(duì)象是否已經(jīng)存在
if(sybDB==null){
// 這是一個(gè)同步鎖, 也就是對(duì)于該對(duì)象的操作,被強(qiáng)制為同步的,同步也就是說(shuō),一次只能有一個(gè)用戶(hù)去操作它,其余的請(qǐng)求都需要排隊(duì),就像我們?cè)谑程么蝻堃粯?,要一個(gè)一個(gè)打
synchronized (lock) {
// 如果這個(gè)對(duì)象為空就創(chuàng)建一個(gè)
if(sybDB==null){
sybDB=new DataBaseDS();
}
}
}// 將結(jié)果返回
return sybDB;
}
線程同步主要有以下種方法(示例中是實(shí)現(xiàn)計(jì)數(shù)的功能):
1、同步方法,即使用synchronized關(guān)鍵字修飾方法,例如:
public?synchronized?void?add(int?c){...}
2、同步代碼塊,即有synchronized關(guān)鍵字修飾的語(yǔ)句塊,例如:
public?void?addAndGet(int?c){
synchronized(this){
count?+=?c;
}
}
3、使用特殊域變量(volatile)實(shí)現(xiàn)線程同步,該方法不能保證絕對(duì)的同步。
例如:private?volatile?int?count?=?0;
4、使用鎖實(shí)現(xiàn)線程同步,例如:
private?Lock?lock?=?new?ReentrantLock();
public?void?add(int?c)?{??
lock.lock();//上鎖??
try{??
count?+=?c;??
}finally{??
lock.unlock();//解鎖??
}??
}
5、使用原子變量實(shí)現(xiàn)線程同步,在java的util.concurrent.atomic包中提供了創(chuàng)建了原子類(lèi)型變量的工具類(lèi),例如:
private?AtomicInteger?count=?new?AtomicInteger(1);
public?void?add(int?c)?{
count.addAndGet(c);
}
6、使用局部變量實(shí)現(xiàn)線程同步,如果使用ThreadLocal管理變量,則每一個(gè)使用該變量的線程都獲得該變量的副本, 副本之間相互獨(dú)立,這樣每一個(gè)線程都可以隨意修改自己的變量副本,而不會(huì)對(duì)其他線程產(chǎn)生影響。
ThreadLocal 類(lèi)的常用方法
new ThreadLocalT() : 創(chuàng)建一個(gè)線程本地變量
get() : 返回此線程局部變量的當(dāng)前線程副本中的值
initialValue() : 返回此線程局部變量的當(dāng)前線程的"初始值"
set(T value) : 將此線程局部變量的當(dāng)前線程副本中的值設(shè)置為value
示例代碼:
private?static?ThreadLocalInteger?count=?new?ThreadLocalInteger(){
@Override
protected?Integer?initialValue(){?
return?1;
}
};????????????
public?void?add(int?c){
count.set(count.get()?+?c);
}
7、使用阻塞隊(duì)列實(shí)現(xiàn),例如LinkedBlockingQueue,具體使用可百度LinkedBlockingQueue的用法或查看java文檔。
1。同步代碼塊:
synchronized(同一個(gè)數(shù)據(jù)){} 同一個(gè)數(shù)據(jù):就是N條線程同時(shí)訪問(wèn)一個(gè)數(shù)據(jù)。
2。
同步方法:
public synchronized 數(shù)據(jù)返回類(lèi)型 方法名(){}
就
是使用 synchronized 來(lái)修飾某個(gè)方法,則該方法稱(chēng)為同步方法。對(duì)于同步方法而言,無(wú)需顯示指定同步監(jiān)視器,同步方法的同步監(jiān)視器是
this
也就是該對(duì)象的本身(這里指的對(duì)象本身有點(diǎn)含糊,其實(shí)就是調(diào)用該同步方法的對(duì)象)通過(guò)使用同步方法,可非常方便的將某類(lèi)變成線程安全的類(lèi),具有如下特征:
1,該類(lèi)的對(duì)象可以被多個(gè)線程安全的訪問(wèn)。
2,每個(gè)線程調(diào)用該對(duì)象的任意方法之后,都將得到正確的結(jié)果。
3,每個(gè)線程調(diào)用該對(duì)象的任意方法之后,該對(duì)象狀態(tài)依然保持合理狀態(tài)。
注:synchronized關(guān)鍵字可以修飾方法,也可以修飾代碼塊,但不能修飾構(gòu)造器,屬性等。
實(shí)現(xiàn)同步機(jī)制注意以下幾點(diǎn): 安全性高,性能低,在多線程用。性能高,安全性低,在單線程用。
1,不要對(duì)線程安全類(lèi)的所有方法都進(jìn)行同步,只對(duì)那些會(huì)改變共享資源方法的進(jìn)行同步。
2,如果可變類(lèi)有兩種運(yùn)行環(huán)境,當(dāng)線程環(huán)境和多線程環(huán)境則應(yīng)該為該可變類(lèi)提供兩種版本:線程安全版本和線程不安全版本(沒(méi)有同步方法和同步塊)。在單線程中環(huán)境中,使用線程不安全版本以保證性能,在多線程中使用線程安全版本.
線程通訊:
為什么要使用線程通訊?
當(dāng)
使用synchronized
來(lái)修飾某個(gè)共享資源時(shí)(分同步代碼塊和同步方法兩種情況),當(dāng)某個(gè)線程獲得共享資源的鎖后就可以執(zhí)行相應(yīng)的代碼段,直到該線程運(yùn)行完該代碼段后才釋放對(duì)該
共享資源的鎖,讓其他線程有機(jī)會(huì)執(zhí)行對(duì)該共享資源的修改。當(dāng)某個(gè)線程占有某個(gè)共享資源的鎖時(shí),如果另外一個(gè)線程也想獲得這把鎖運(yùn)行就需要使用wait()
和notify()/notifyAll()方法來(lái)進(jìn)行線程通訊了。
Java.lang.object 里的三個(gè)方法wait() notify() notifyAll()
wait方法導(dǎo)致當(dāng)前線程等待,直到其他線程調(diào)用同步監(jiān)視器的notify方法或notifyAll方法來(lái)喚醒該線程。
wait(mills)方法
都是等待指定時(shí)間后自動(dòng)蘇醒,調(diào)用wait方法的當(dāng)前線程會(huì)釋放該同步監(jiān)視器的鎖定,可以不用notify或notifyAll方法把它喚醒。
notify()
喚醒在同步監(jiān)視器上等待的單個(gè)線程,如果所有線程都在同步監(jiān)視器上等待,則會(huì)選擇喚醒其中一個(gè)線程,選擇是任意性的,只有當(dāng)前線程放棄對(duì)該同步監(jiān)視器的鎖定后,也就是使用wait方法后,才可以執(zhí)行被喚醒的線程。
notifyAll()方法
喚醒在同步監(jiān)視器上等待的所有的線程。只用當(dāng)前線程放棄對(duì)該同步監(jiān)視器的鎖定后,才可以執(zhí)行被喚醒的線程