-
CPU-高速緩存-主存
在主流計(jì)算機(jī)的設(shè)計(jì)中,CPU的運(yùn)算速度比主內(nèi)存的讀寫(xiě)速度要快得多,這就使得CPU在訪問(wèn)內(nèi)存時(shí)要花很長(zhǎng)時(shí)間來(lái)等待內(nèi)存的操作,這種空等造成了系統(tǒng)整體性能的下降。為了解決這種速度上的不匹配問(wèn)題,我們?cè)贑PU與主內(nèi)存之間加入了比主內(nèi)存要快的SRAM(Static Ram,靜態(tài)存儲(chǔ)器)。SRAM儲(chǔ)存了主內(nèi)存的映象,使CPU可以直接通過(guò)訪問(wèn)SRAM來(lái)完成數(shù)據(jù)的讀寫(xiě)。由于SRAM的速度與CPU的速度相當(dāng),從而大大縮短了數(shù)據(jù)讀寫(xiě)的等待時(shí)間,系統(tǒng)的整體速度也自然得到提高。 高速緩存即 Cache,就是指介于CPU與主內(nèi)存之間的高速存儲(chǔ)器(通常由靜態(tài)存儲(chǔ)器SRAM構(gòu)成)。
Cache的工作原理是基于程序訪問(wèn)的局部性。依據(jù)局部性原理,可以在主存和CPU通用寄存器之間設(shè)置一個(gè)高速的容量相對(duì)較小的存儲(chǔ)器,把正在執(zhí)行的指令地址附近的一部分指令或數(shù)據(jù)從主存調(diào)入這個(gè)存儲(chǔ)器,供CPU在一段時(shí)間內(nèi)使用。這對(duì)提高程序的運(yùn)行速度有很大的作用。這個(gè)介于主存和CPU之間的高速小容量存儲(chǔ)器稱(chēng)作高速緩沖存儲(chǔ)器(Cache)。
順序流程.jpg
每個(gè)線程都有自己的高速緩存,在多線并發(fā)過(guò)程中,會(huì)導(dǎo)致CPU訪問(wèn)到高速緩存的數(shù)據(jù)不一致,從而導(dǎo)致線程并發(fā)問(wèn)題。
-
并發(fā)的三個(gè)概念
-
原子性
執(zhí)行不可以分割,代碼1、2、3要么都執(zhí)行,要么都不執(zhí)行。例如A 匯錢(qián)給 B,需要保證在數(shù)據(jù)修改時(shí),A和B的賬戶(hù)同時(shí)修改(成功),或者同時(shí)不修改(不成功)。
-
可見(jiàn)性
當(dāng)多線程訪問(wèn)同一個(gè)共享變量時(shí),一個(gè)線程修改變量的值,保證了其他線程及時(shí)可以看到變量的修改,例如:volatile 關(guān)鍵字,保證變量修改的可見(jiàn)性。
-
有序性
程序執(zhí)行的順序按照代碼的先后順序執(zhí)行:在很多編譯器中,會(huì)對(duì)處理指令重新排序,提高執(zhí)行效率,但是只會(huì)排序于數(shù)據(jù)無(wú)關(guān)的語(yǔ)句,保持線程內(nèi)結(jié)果一致性。例如:java內(nèi)存模型中,有happens-before原則。在java中,重新指令排序不僅僅涉及到結(jié)果的一致性,還涉及到GC執(zhí)行調(diào)用--重新排序的指令段執(zhí)行時(shí),觸發(fā)GC會(huì)導(dǎo)致垃圾回收的不可靠,因此GC的觸發(fā)并不是在所有的代碼執(zhí)行時(shí)都可以觸發(fā),而是存在一定的安全區(qū)域,在安全區(qū)域內(nèi)才能觸發(fā)GC,有興趣的同事可以去深入了解一下。
-
-
線程同步
-
Synchronized修飾
synchronized可以保證變量的修改可見(jiàn)性和原子性,當(dāng)前線程可以訪問(wèn)該變量,其他線程被阻塞住
-
public synchronized void addSession(){
mSessionCount++;
}
public void removeSession(){
synchronized (this) {
mSessionCount--;
}
}
-
volatile (保證可見(jiàn)性,不保證原子性)
volatile本質(zhì)是:jvm當(dāng)前變量在寄存器中的值是不確定的,需要從主存中讀取
private volatile int mSessionCount;
-
原子類(lèi)(AtomicInterger, AtomicBoolean ......)
-
lock鎖
lock鎖的狀態(tài)是可見(jiàn)的,lock鎖的類(lèi)型比較多:可重入鎖,讀寫(xiě)鎖,使用方法更加靈活。lock必須在finally釋放,否則可能會(huì)造成異常情況下,鎖無(wú)法釋放
ReentrantLock mLock = new ReentrantLock();
public void addSession(){
mLock.lock();
try{
mSessionCount++;
}finally{
mLock.unlock();
}
}
-
容器類(lèi)
ConcurrentHashMap BlockingQueue等數(shù)據(jù)結(jié)構(gòu)
在java中,由于hashmap等容器是非線程安全的,java提供對(duì)用concurrentXXX數(shù)據(jù)結(jié)構(gòu),多線程安全,而且效率非常高,有興趣的同事可以去看下
-
ThreadLocal
ThreadLocal比較常用,其本質(zhì)不是線程同步,而是以空間換時(shí)間,每個(gè)線程維護(hù)自己的副本(在后臺(tái)并發(fā)時(shí)比較有用)
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();
}
測(cè)試:
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();
}
結(jié)果:
session count : 1
session count : 1
session count : 1
session count : 1
session count : 1
其原因是ThreadLocal在ThreadLocalMap中為每個(gè)線程保存了副本,map的key就是Thread本身,ThreadLocalMap持有ThreadLocal的弱引用,保證線程釋放資源時(shí)的內(nèi)存泄露問(wèn)題(ThreadLoacl變量建議設(shè)置成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);
}
-
總結(jié)
以上簡(jiǎn)單描述了CPU-Cache-主存模型,并發(fā)和線程同步,具體的實(shí)現(xiàn)大家可以多看看源碼和第三方開(kāi)源框架,里面很多設(shè)計(jì)非常值得我們學(xué)習(xí)和借鑒。
