多線程不一定比單線程快
當累加操作少于百萬次時,單線程執行的速度會比多線程執行的速度快,因為線程有建立和上下文切換的開銷
vmstat的cs表示每秒上下文切換的次數
如何減少多線程上下文切換次數
使用無鎖并發程式設計,CAS算法,使用最少線程和使用協程
死鎖
死鎖樣例:
public class DeadLockDemo {
/** A鎖 */
private static String A = "A";
/** B鎖 */
private static String B = "B";
public static void main(String[] args) {
new DeadLockDemo().deadLock();
}
private void deadLock() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (A) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println("1");
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (B) {
synchronized (A) {
System.out.println("2");
}
}
}
});
t1.start();
t2.start();
}
}
現實場景中,比如t1拿到鎖之後,因為一些異常情況沒有釋放鎖(死循環)。又或者是t1拿到一個資料庫鎖,釋放鎖的時候抛出了異常,沒釋放掉,就會出現死鎖
避免死鎖的常用辦法
- 避免一個線程同時擷取多個鎖。
- 避免一個線程在鎖内同時占用多個資源,盡量保證每個鎖隻占用一個資源。
- 嘗試使用定時鎖,使用lock.tryLock(timeout)來替代使用内部鎖機制。
- 對于資料庫鎖,加鎖和解鎖必須在一個資料庫連接配接裡,否則會出現解鎖失敗的情況。
volatile的應用
volatile是輕量級的synchronized,它在多處理器開發中保證了共享變量的可見性
可見性的意思是當一個線程修改一個共享變量時,另外一個線程能讀到這個修改的值
volatile定義與實作原理
java程式設計語言允許線程通路共享變量,為了確定共享變量能被準确和一緻地更新,線程應該確定通過排他鎖單獨獲得這個變量。
Java語言提供了volatile,在某些情況下比鎖要更加友善。如果一個字段被聲明成volatile,Java線程記憶體模型確定所有線程看到這個變量的值是一緻的
volatile會增加Lock字首指令
Lock字首指令會引起處理器緩存回寫到記憶體
一個處理器的緩存回寫到記憶體會導緻其他處理器的緩存無效
synchronized
JVM基于進入和退出Monitor對象來實作方法同步和代碼塊同步
代碼塊同步是使用monitorenter和monitorexit指令實作的,而方法同步是使用另外一種方式實作的
鎖的4種狀态
無鎖,偏向鎖,輕量級鎖,重量級鎖
鎖可以更新不能降級,目的是為了提高獲得鎖和釋放鎖的效率
單例模式中的雙重檢測鎖
public class UnsafeLazyInitialization {
private static Instance instance;
public static Instance getInstance() {
if (instance == null) // 1:A線程執行
instance = new Instance(); // 2:B線程執行
return instance;
}
static class Instance {
}
}