-
CPU-高速緩存-主存
在主流計算機的設計中介陶,CPU的運算速度比主內存的讀寫速度要快得多学搜,這就使得CPU在訪問內存時要花很長時間來等待內存的操作娃豹,這種空等造成了系統(tǒng)整體性能的下降憔涉。為了解決這種速度上的不匹配問題讨勤,我們在CPU與主內存之間加入了比主內存要快的SRAM(Static Ram箭跳,靜態(tài)存儲器)。SRAM儲存了主內存的映象潭千,使CPU可以直接通過訪問SRAM來完成數(shù)據(jù)的讀寫谱姓。由于SRAM的速度與CPU的速度相當,從而大大縮短了數(shù)據(jù)讀寫的等待時間刨晴,系統(tǒng)的整體速度也自然得到提高屉来。 高速緩存即 Cache,就是指介于CPU與主內存之間的高速存儲器(通常由靜態(tài)存儲器SRAM構成)狈癞。
Cache的工作原理是基于程序訪問的局部性茄靠。依據(jù)局部性原理,可以在主存和CPU通用寄存器之間設置一個高速的容量相對較小的存儲器蝶桶,把正在執(zhí)行的指令地址附近的一部分指令或數(shù)據(jù)從主存調入這個存儲器慨绳,供CPU在一段時間內使用。這對提高程序的運行速度有很大的作用真竖。這個介于主存和CPU之間的高速小容量存儲器稱作高速緩沖存儲器(Cache)脐雪。
每個線程都有自己的高速緩存,在多線并發(fā)過程中恢共,會導致CPU訪問到高速緩存的數(shù)據(jù)不一致战秋,從而導致線程并發(fā)問題。
-
并發(fā)的三個概念
-
原子性
執(zhí)行不可以分割讨韭,代碼1脂信、2癣蟋、3要么都執(zhí)行,要么都不執(zhí)行狰闪。例如A 匯錢給 B梢薪,需要保證在數(shù)據(jù)修改時,A和B的賬戶同時修改(成功)尝哆,或者同時不修改(不成功)秉撇。
-
可見性
當多線程訪問同一個共享變量時,一個線程修改變量的值秋泄,保證了其他線程及時可以看到變量的修改琐馆,例如:volatile 關鍵字,保證變量修改的可見性恒序。
-
有序性
程序執(zhí)行的順序按照代碼的先后順序執(zhí)行:在很多編譯器中瘦麸,會對處理指令重新排序,提高執(zhí)行效率歧胁,但是只會排序于數(shù)據(jù)無關的語句滋饲,保持線程內結果一致性。例如:java內存模型中喊巍,有happens-before原則屠缭。在java中,重新指令排序不僅僅涉及到結果的一致性崭参,還涉及到GC執(zhí)行調用--重新排序的指令段執(zhí)行時呵曹,觸發(fā)GC會導致垃圾回收的不可靠,因此GC的觸發(fā)并不是在所有的代碼執(zhí)行時都可以觸發(fā)何暮,而是存在一定的安全區(qū)域奄喂,在安全區(qū)域內才能觸發(fā)GC,有興趣的同事可以去深入了解一下海洼。
-
-
線程同步
-
Synchronized修飾
synchronized可以保證變量的修改可見性和原子性跨新,當前線程可以訪問該變量,其他線程被阻塞住
-
public synchronized void addSession(){
mSessionCount++;
}
public void removeSession(){
synchronized (this) {
mSessionCount--;
}
}
-
volatile (保證可見性,不保證原子性)
volatile本質是:jvm當前變量在寄存器中的值是不確定的,需要從主存中讀取
private volatile int mSessionCount;
-
原子類(AtomicInterger, AtomicBoolean ......)
-
lock鎖
lock鎖的狀態(tài)是可見的坏逢,lock鎖的類型比較多:可重入鎖域帐,讀寫鎖,使用方法更加靈活词疼。lock必須在finally釋放俯树,否則可能會造成異常情況下,鎖無法釋放
ReentrantLock mLock = new ReentrantLock();
public void addSession(){
mLock.lock();
try{
mSessionCount++;
}finally{
mLock.unlock();
}
}
-
容器類
ConcurrentHashMap BlockingQueue等數(shù)據(jù)結構
在java中贰盗,由于hashmap等容器是非線程安全的,java提供對用concurrentXXX數(shù)據(jù)結構阳欲,多線程安全舵盈,而且效率非常高陋率,有興趣的同事可以去看下
-
ThreadLocal
ThreadLocal比較常用,其本質不是線程同步秽晚,而是以空間換時間瓦糟,每個線程維護自己的副本(在后臺并發(fā)時比較有用)
private static ThreadLocal<Integer> sCount = new ThreadLocal<Integer>();
public void addSession(int count){
count = ((sCount.get() == null) ? 0 : sCount.get()) + count;
sCount.set(count);
}
public int getSession(){
return sCount.get();
}
測試:
for(int i = 0; i < 5; i++){
new Thread(new Runnable() {
public void run() {
s.addSession(1);
System.out.println("session count : " + s.getSession());
}
}).start();
}
結果:
session count : 1
session count : 1
session count : 1
session count : 1
session count : 1
其原因是ThreadLocal在ThreadLocalMap中為每個線程保存了副本,map的key就是Thread本身赴蝇,ThreadLocalMap持有ThreadLocal的弱引用菩浙,保證線程釋放資源時的內存泄露問題(ThreadLoacl變量建議設置成private static),ThreadLocal:
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
/**
* Removes the current thread's value for this thread-local
* variable. If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim. This may result in multiple invocations of the
* {@code initialValue} method in the current thread.
*
* @since 1.5
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
-
總結
以上簡單描述了CPU-Cache-主存模型句伶,并發(fā)和線程同步劲蜻,具體的實現(xiàn)大家可以多看看源碼和第三方開源框架,里面很多設計非常值得我們學習和借鑒考余。