并發(fā)編程的優(yōu)點和缺點
優(yōu)點:
提升性能
將多核CPU的計算能力發(fā)揮到機制,性能得到提升
業(yè)務(wù)適用
并行計算會比串行計算更適應(yīng)業(yè)務(wù)需求,而并發(fā)編程更適用于該模型
缺點:
頻繁上線文切換
前提是:線程數(shù)量 > CPU核數(shù)
線程安全
數(shù)據(jù)的可見性、原子性、有序性
無鎖并發(fā)編程
Runnable和Callable的區(qū)別
- Runnable從JDK1.1就有了,而Callble是JDK1.5之后增加的
- Callable執(zhí)行方法是call(),Runnable執(zhí)行方法是run()
- Callable任務(wù)執(zhí)行后可返回值,而Runnable任務(wù)是不能返回值(void)
- call方法可以拋出異常,run方法不可以
- 運行Callable任務(wù)可拿到Future對象,即異步計算結(jié)果:
- 它提供了檢查計算是否完成的方法,以等待計算的完成,并檢索計算的結(jié)果
- 通過Future對象可以了解任務(wù)執(zhí)行情況,可取消任務(wù)的執(zhí)行,還可以獲取執(zhí)行結(jié)果
- 使用線程池方式運行:
- Runnable用ExecutorService的execute方法
- Callable使用submit方法
為什么使用鎖
并發(fā)操作同一資源
資源競爭、保護
資源胡釵
并發(fā)編程的產(chǎn)生
Java中鎖的種類
- synchronized
- synchronized關(guān)鍵字,語義層面的定義及使用
- 隱式地獲取鎖,將鎖的獲取和釋放固化,即先獲取在釋放當前鎖
- Lock(JDK1.5)
- 對比:
- 非阻塞方式獲取鎖
- 獲取鎖喉可被中斷
- 獲取鎖設(shè)置超時機制
synchronize的用法

Java對象的組成

synchronize 鎖升級
偏向鎖即有一個線程A訪問帶有鎖的同步代碼塊時,會在對象頭的mark word記錄線程的ID,當有線程訪問同步塊時,會比較mark word中的線程id,和當前線程是否一致,如果還是線程A則直接獲取到鎖,當有一個B訪問時,會嘗試CAS將對象頭中的mark wor替換為指向鎖記錄指針,如果替換成功,則當前線程獲得到鎖,如果替換失敗則說明有其他線程在競爭鎖,當前線程使用自旋來獲取鎖,當自旋次數(shù)達到一定次數(shù)時,就會升級成為重量級鎖,由操作系統(tǒng)Mutexlock實現(xiàn)線程間的切換
輕量鎖就是偏向鎖+自旋
? 自旋的次數(shù)可以用個jvm參數(shù)設(shè)置,但是在jdk1.7之后,就不建議修改這個參數(shù)了,因為程序進行了優(yōu)化,鎖自動優(yōu)化,鎖彈性升級,它自動判斷大部分情況需要多少自旋次數(shù)能解決問題,不斷升級自旋次數(shù)

Lock
- Lock概念
- 是一個接口,它定義和規(guī)范了"獲取和釋放鎖",Lock接口的實現(xiàn)基本都是通過聚合了一個同步器的子類來完成線程訪問控制的
- Lock接口方法
- lock() 獲取鎖
- lockInterruptibly() 可中斷地進行獲取鎖
- tryLock() 非阻塞地嘗試獲取鎖
- tryLock(long time, TimeUnit unit) 有超時時間獲取鎖
- unlock() 釋放鎖
- Condition newCondition獲取等待通知組件,該組件與當前鎖綁定,只有當前線程獲取鎖才可以使用組件await(),await()調(diào)用后釋放鎖
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class TestLock implements Lock {
// 獲取鎖
public void lock() {
}
// 可中斷地進行獲取鎖
public void lockInterruptibly() throws InterruptedException {
}
// 非阻塞地嘗試獲取鎖
public boolean tryLock() {
return false;
}
// 有超時時間獲取鎖
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
// 釋放鎖
public void unlock() {
}
// 獲取等待通知組件,該組件與當前鎖綁定,只有當前線程獲取鎖才可以使用組件await(),await()調(diào)用后釋放鎖
public Condition newCondition() {
return null;
}
}
Condition
**await(): **造成當前線程在接收到信號或被中斷之前一直處于等待狀態(tài)。
**await(long time, TimeUnit unit): **造成當前線程在接收到信號、被中斷或達到指定等待時間之前一直處于等待狀態(tài)。
**long awaitNanos(long nanosTimeout): **造成當前線程在接收到信號、被中斷或達到指定等待時間之前一直處于等待狀態(tài)。返回值表示剩余時間,如果在nanosTimeout之前喚醒,那么返回值= nanosTimeout - 消耗時間,如果返回值 <= 0,則可以認定它已經(jīng)超時了。
-
**awaitUninterruptibly(): **造成當前線程在接收到信號之前一直處于等待狀態(tài)。
- 注意:該方法對中斷不敏感。
**boolean awaitUntil(Date deadline): **造成當前線程在接收到信號、被中斷或到達指定最后期限之前一直處于等待狀態(tài)。如果沒有到指定時間就被通知,則返回true,否則表示到了指定時間,返回false。
**void signal(): **喚醒一個等待線程。該線程從等待放放返回前必須獲得與Condition相關(guān)的鎖。
**void signalAll(): **喚醒所有等待線程,能夠從等待放放返回的線程,必須獲取與Condition相關(guān)的鎖
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TestCondition {
public static void main(String[] args) {
final ReentrantLock reentrantLock = new ReentrantLock();
final Condition condition = reentrantLock.newCondition();
new Thread(() -> {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName() + "拿到了鎖");
System.out.println(Thread.currentThread().getName() + "等待信號");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到信號");
reentrantLock.unlock();
}, "線程1").start();
new Thread(() -> {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName() + "拿到了鎖");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "發(fā)出信號");
condition.signalAll();
reentrantLock.unlock();
}, "線程2").start();
}
}
AQS 隊列同步器
- AQS:AbstractQueuedSynchronizer 隊列同步器
- 用來構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架
- 用int成員變量表示同步狀態(tài)
- 通過內(nèi)置的FIFO(先進先出)隊列來完成資源獲取線程的排隊工作
- 大部分同步需求的基礎(chǔ)
- 同步狀態(tài):
- getState()
- setSate(int newState)
- compareAndSetState(int expect, int update)
- 鎖是面向使用者的,它定義了使用者與鎖交互的接口(比如可以允許兩個線程并行訪問),隱藏了實現(xiàn)細節(jié)
- 同步器(AQS)面向的是鎖的創(chuàng)造者OR開發(fā)者,它提供了便捷的工具和方法,將同步狀態(tài),線程排隊,等待及喚醒等底層操作封裝起來,讓開發(fā)者使用更便捷
- 鎖和同步器很多地隔離了使用者和實現(xiàn)者所需關(guān)注的范圍及各自關(guān)注的邏輯細節(jié)
volatile
- 保證不同線程對統(tǒng)一變量操作時的可見性
- 禁止指令重排序
MESI協(xié)議
- 將當前處理器緩存行的數(shù)據(jù)寫會系統(tǒng)內(nèi)存
- 這個寫回內(nèi)存的操作會使其他CPU里緩存了該內(nèi)存地址的數(shù)據(jù)無效
- volatile下的MESI:
- Lock前綴的指令會引起處理器緩存寫回內(nèi)存
- 一個處理器的緩存回寫到內(nèi)存會導(dǎo)致其他處理器的緩存失效
- 當處理器發(fā)現(xiàn)本地緩存失效后,就會從內(nèi)存中重讀該變量數(shù)據(jù),既可以獲取當前最新值

AQS實現(xiàn)原理
- 同步器依賴內(nèi)部的隊列完成同步狀態(tài)的管理,當線程獲取同步狀態(tài)失敗時,同步器會將當前線程與等待狀態(tài)等信息構(gòu)造成為一個節(jié)點(Node)將其放入同步隊列,同時會阻塞當前線程。同步狀態(tài)釋放時,喚醒首節(jié)點中的線程,使其再次嘗試獲取同步狀態(tài),
- 主要包括:
- 同步隊列
- 獨占式同步狀態(tài)獲取與釋放
- 共享式同步狀態(tài)獲取與釋放
- 超時獲取同步狀態(tài)等同步器的核心數(shù)據(jù)結(jié)構(gòu)與模板方法
同步隊列
-
同步隊列中的節(jié)點(Node):
- 保存獲取同步狀態(tài)失敗線程的引用,等待狀態(tài)以及前置和后置節(jié)點
-
包含的字段:
- prev 前置節(jié)點
- next 后置節(jié)點
- thread 獲取同步狀態(tài)的線程
- waitStatus 等待狀態(tài)
- nextWaiter 等待隊列中的后置節(jié)點
AQS同步隊列.png
