多個(gè)線程可以共享進(jìn)程的堆和方法區(qū)資源,既多個(gè)線程共享類變量。多個(gè)線程共享一個(gè)進(jìn)程的變量時(shí),如果線程對這個(gè)變量只有讀操作,沒有更新操作則這個(gè)線程沒有線程安全問題。如果線程需要對這個(gè)變量進(jìn)行修改操作,則可能會(huì)因?yàn)閿?shù)據(jù)更新不及時(shí)導(dǎo)致變量信息不準(zhǔn)確而引發(fā)線程不安全。
當(dāng)多個(gè)線程對同一個(gè)資源進(jìn)行操作的時(shí)候就會(huì)有線程安全。解決線程安全的核心思想就是加鎖。加鎖有兩種方式:1.JVM提供的鎖,就是synchronized鎖,即同步代碼和同步代碼塊 2.jdk提供的各種鎖,如lock。
在JDK1.1版本中,所有的集合都是線程安全的。但是在1.2及以后的版本中就出現(xiàn)了一些線程不安全的集合,為什么版本升級反而會(huì)出現(xiàn)一些線程不安全的集合呢?因?yàn)榫€程不安全的集合普遍比線程安全的集合效率高的多。隨著業(yè)務(wù)的發(fā)展,特別是在WEB應(yīng)用中,為了提高用戶體驗(yàn),減少用戶的等待時(shí)間,頁面的響應(yīng)速度(也就是效率)是優(yōu)先考慮的。而且對線程不安全的集合加鎖以后也能達(dá)到安全的效果(但是效率會(huì)低,因?yàn)闀?huì)有鎖的獲取以及等待)。其實(shí)在JDK源碼中相同效果的集合線程安全的比線程不安全的就多了一個(gè)同步機(jī)制,但是效率上卻低了不止一點(diǎn)點(diǎn),因?yàn)樾实停砸呀?jīng)不太建議使用了。下面列舉一些常用的線程安全的集合。
Vector:就比ArrayList多了個(gè)同步化機(jī)制。
HashTable:就比HashMap多了個(gè)線程安全。
ConcurrentHashMap:是一種高效但是線程安全的集合。
Stack:棧,線程安全,繼承與Vector。
1)修飾實(shí)例方法: 作用于當(dāng)前對象實(shí)例加鎖,進(jìn)入同步代碼前要獲得 當(dāng)前對象實(shí)例的鎖
synchronized void method() {
? //業(yè)務(wù)代碼
}
2)修飾靜態(tài)方法: 也就是給當(dāng)前類加鎖,會(huì)作用于類的所有對象實(shí)例 ,進(jìn)入同步代碼前要獲得 當(dāng)前 class 的鎖。因?yàn)殪o態(tài)成員不屬于任何一個(gè)實(shí)例對象,是類成員( static 表明這是該類的一個(gè)靜態(tài)資源,不管 new 了多少個(gè)對象,只有一份)。所以,如果一個(gè)線程 A 調(diào)用一個(gè)實(shí)例對象的非靜態(tài) synchronized 方法,而線程 B 需要調(diào)用這個(gè)實(shí)例對象所屬類的靜態(tài) synchronized 方法,是允許的,不會(huì)發(fā)生互斥現(xiàn)象,因?yàn)樵L問靜態(tài) synchronized 方法占用的鎖是當(dāng)前類的鎖,而訪問非靜態(tài) synchronized 方法占用的鎖是當(dāng)前實(shí)例對象鎖。
synchronized void staic method() {
? //業(yè)務(wù)代碼
}
3)修飾代碼塊 :指定加鎖對象,對給定對象/類加鎖。synchronized(this|object) 表示進(jìn)入同步代碼庫前要獲得給定對象的鎖。synchronized(類.class) 表示進(jìn)入同步代碼前要獲得 當(dāng)前 class 的鎖
synchronized(this) {
? //業(yè)務(wù)代碼
}
總結(jié):synchronized 關(guān)鍵字加到 static 靜態(tài)方法和 synchronized(class) 代碼塊上都是是給 Class 類上鎖。
synchronized 關(guān)鍵字加到實(shí)例方法上是給對象實(shí)例上鎖。
盡量不要使用 synchronized(String a) 因?yàn)?JVM 中,字符串常量池具有緩存功能!
構(gòu)造方法不能使用 synchronized 關(guān)鍵字修飾。構(gòu)造方法本身就屬于線程安全的,不存在同步的構(gòu)造方法一說。
public class Singleton {
private static Singleton uniqueInstance;
// 私有化構(gòu)造方法
private Singleton() {
}
// 提供getInstance方法
public static Singleton getInstance() {
//先判斷對象是否已經(jīng)實(shí)例過,沒有實(shí)例化過才進(jìn)入加鎖代碼
if (uniqueInstance == null) {
//類對象加鎖
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
同步:功能調(diào)用時(shí),在沒有得到結(jié)果之前,該調(diào)用就不返回或繼續(xù)執(zhí)行后續(xù)操作。這時(shí)程序是阻塞的,只有接收到返回的值或消息后才往下執(zhí)行其他的命令。因此,簡單來說,同步就是必須一件一件做事,等前一件事做完了才能做完下一件事。
異步:與同步相對,當(dāng)一個(gè)異步過程調(diào)用發(fā)出后,調(diào)用者在沒有得到結(jié)果之前,就可以繼續(xù)執(zhí)行后續(xù)操作,當(dāng)這個(gè)調(diào)用完成后,一般通過狀態(tài)或者回調(diào)來通知調(diào)用者。
同步塊,這意味著同步塊之外的代碼是異步執(zhí)行的,這比同步整個(gè)方法更提升代 碼的效率。請知道一條原則:同步的范圍越小越好。
當(dāng)一個(gè)線程需要調(diào)用對象的wait()方法的時(shí)候,這個(gè)線程必須擁有該對象的鎖,接著它就會(huì)釋放這個(gè)對象鎖并進(jìn)入等待狀態(tài)直到其他線程調(diào)用這個(gè)對象上的notify()方法。同樣的,當(dāng)一個(gè)線程需要調(diào)用對象的notify()方法時(shí),它會(huì)釋放這個(gè)對象的鎖,以便其他在等待的線程就可以得到這個(gè)對象鎖。由于所有的這些方法都需要線程持有對象的鎖,這樣就只能通過同步來實(shí)現(xiàn),所以他們只能在同步方法或者同步塊中被調(diào)用。
同步就是協(xié)同步調(diào),按預(yù)定的先后次序進(jìn)行運(yùn)行。如:你說完,我再說。這里的同步千萬不要理解成那個(gè)同時(shí)進(jìn)行,應(yīng)是指協(xié)同、協(xié)助、互相配合。線程同步是指多線程通過特定的設(shè)置(如互斥量,事件對象,臨界區(qū))來控制線程之間的執(zhí)行順序(即所謂的同步)也可以說是在線程之間通過同步建立起執(zhí)行順序的關(guān)系,如果沒有同步,那線程之間是各自運(yùn)行各自的!
線程互斥是指對于共享的進(jìn)程系統(tǒng)資源,在各單個(gè)線程訪問時(shí)的排它性。當(dāng)有若干個(gè)線程都要使用某一共享資源時(shí),任何時(shí)刻最多只允許一個(gè)線程去使用,其它要使用該資源的線程必須等待,直到占用資源者釋放該資源。線程互斥可以看成是一種特殊的線程同步(下文統(tǒng)稱為同步)。