多線程筆記

1.多線程的建立方式有兩種   a 實現Runnable的接口 實現他的run的方法 建議使用這種 由於接口能夠實現多繼承   b 集成Thread 的抽象類,重寫父類的 run的方法。 2.run() 與start()的區別   調用start方法方可啓動線程,而run方法只是thread的一個普通方法調用,仍是在主線程裏執行。   把須要並行處理的代碼放在run()方法中,start()方法啓動線程將自動調用 run()方法,這是由jvm的內存機制規定的。而且run()方法必須是public訪問權限,返回值類型爲void. 3.多線程的Synchronized   a Synchronized 須要鎖住對象,否則的話仍是會互斥。   b 靜態方法和實例方法同步的話須要鎖住 class自己,(靜態方法須要同步只有鎖住字節碼的對象才能夠 Object.class)   c 要用到共同數據,最好所有在同一個類中實現,既能夠達到高內聚,也能夠實現同步鎖。 4.sleep和wait的區別   a sleep來自Thread類,和wait來自Object類。   b 最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其餘線程可使用同步控制塊或者方法。   c wait等待的線程能夠用 notify 喚醒。這個能夠用while 循環一塊兒使用能夠防止僞喚醒,由於有時候沒用調用notify的時候也能夠喚醒線程。   d 在wait()/notify()機制中,不要使用全局對象,字符串常量等。應該使用對應惟一的對象。   public void method(){ while(true){ wait(); ........ notify(); }   } 5.線程共享變量   a ThreadLocal 不是用來解決共享對象的多線程訪問問題的,通常狀況下,經過ThreadLocal.set() 到線程中的對象是該線程本身使用的對象,其餘線程是不須要訪問的,也訪問不到的。   b 也能夠用HashMap類來實現這個功能,map.put(當前線程,須要該線程共享的值);   c ThreadLocal 的具體實現是 ThreadLocalMap 這個map的具體key是當前線程。該類的set方法與下面相似用來取值;   d 一個ThreadLocal只能放一個變量 若是有多個的話能夠用一個實體封裝。或者新建多個ThreadLocal對象;    public T get() {     //獲取當前執行線程     Thread t = Thread.currentThread();     //取得當前線程的ThreadLocalMap實例     ThreadLocalMap map = getMap(t);     //若是map不爲空,說明該線程已經有了一個ThreadLocalMap實例     if (map != null) {         //map中保存線程的全部的線程本地變量,咱們要去查找當前線程本地變量         ThreadLocalMap.Entry e = map.getEntry(this);         //若是當前線程本地變量存在這個map中,則返回其對應的值         if (e != null)             return (T)e.value;     }     //若是map不存在或者map中不存在當前線程本地變量,返回初始值     return setInitialValue(); } 6.Thread.stop()  方法是不安全的. 釋放該線程所持有的全部的鎖,那麼被保護數據就有可能呈現不一致性,其餘線程在使用這些被破壞的數據時,有可能致使一些很奇怪的應用程序錯誤。 7.如何正確中止線程 關於如何正確中止線程,這篇文章(how to stop thread)給出了一個很好的答案, 總結起來就下面3點(在中止線程時): 1. 使用violate boolean變量來標識線程是否中止 2. 中止線程時,須要調用中止線程的interrupt()方法,由於線程有可能在wait()或sleep(), 提升中止線程的即時性 3. 對於blocking IO的處理,儘可能使用InterruptibleChannel來代替blocking IO 總結: 1.局部變量中的基本數據類型(8種)永遠是線程安全的。 2.局部變量中的對象類型只要不會被其餘線程訪問到,也是線程安全的。 3.一個對象實例被多個線程同時訪問時,他的成員變量就多是線程不安全的。 =========================================================================== java.util.concurrent 線程併發包 (compareAndSet)CAS同步的原理:http://www.blogjava.net/syniii/archive/2010/11/18/338387.html?opt=admin CAS有3個操做數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改成B,不然什麼都不作。 利用CPU的CAS指令,同時藉助JNI來完成Java的非阻塞算法。 CAS看起來很爽,可是會致使「ABA問題」。 CAS算法實現一個重要前提須要取出內存中某時刻的數據,而在下時刻比較並替換,那麼在這個時間差類會致使數據的變化。 好比說一個線程one從內存位置V中取出A,這時候另外一個線程two也從內存中取出A,而且two進行了一些操做變成了B,而後two又將V位置的數據變成A,這時候線程one進行CAS操做發現內存中仍然是A,而後one操做成功。儘管線程one的CAS操做成功,可是不表明這個過程就是沒有問題的。若是鏈表的頭在變化了兩次後恢復了原值,可是不表明鏈表就沒有變化。所以前面提到的原子操做AtomicStampedReference/AtomicMarkableReference就頗有用了。這容許一對變化的元素進行原子操做。 java.util.concurrent.AtomicInteger : AtomicInteger,一個提供原子操做的Integer的類。在Java語言中,++i和i++操做並非線程安全的,在使用的時候,不可避免的會用到synchronized關鍵字。而AtomicInteger則經過一種線程安全的加減操做接口。         採用的是compareAndSet CAS來保證數據的同步,是經過CPU來實現的,比起用synchronized, 這裏的排他時間要短的多. 因此在多線程狀況下性能會比較好. ========================================================================================================= 線程池 參考文檔:http://www.oschina.net/question/565065_86540 線程池的做用:線程池做用就是限制系統中執行線程的數量。根據系統的環境狀況,能夠自動或手動設置線程數量,達到運行的最佳效果;少了浪費了系統資源,多了形成系統擁擠效率不高。用線程池控制線程數量,其餘線程排隊等候。一個任務執行完畢,再從隊列的中取最前面的任務開始執行。若隊列中沒有等待進程,線程池的這一資源處於等待。當一個新任務須要運行時,若是線程池中有等待的工做線程,就能夠開始運行了;不然進入等待隊列。 爲何要用線程池:1.減小了建立和銷燬線程的次數,每一個工做線程均可以被重複利用,可執行多個任務。 2.能夠根據系統的承受能力,調整線程池中工做線線程的數目,防止由於消耗過多的內存,而把服務器累趴下(每一個線程須要大約1MB內存,線程開的越多,消耗的內存也就越大,最後死機)。 Java裏面線程池的頂級接口是Executor,可是嚴格意義上講Executor並非一個線程池,而只是一個執行線程的工具。真正的線程池接口是ExecutorService。 ExecutorService 真正的線程池接口。 ScheduledExecutorService 能和Timer/TimerTask相似,解決那些須要任務重複執行的問題。 ThreadPoolExecutor ExecutorService的默認實現。 ScheduledThreadPoolExecutor 繼承ThreadPoolExecutor的ScheduledExecutorService接口實現,週期性任務調度的類實現。 1. newSingleThreadExecutor 建立一個單線程的線程池。這個線程池只有一個線程在工做,也就是至關於單線程串行執行全部任務。若是這個惟一的線程由於異常結束,那麼會有一個新的線程來替代它。此線程池保證全部任務的執行順序按照任務的提交順序執行。 2.newFixedThreadPool 建立固定大小的線程池。每次提交一個任務就建立一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,若是某個線程由於執行異常而結束,那麼線程池會補充一個新線程。 3. newCachedThreadPool 建立一個可緩存的線程池。若是線程池的大小超過了處理任務所須要的線程, 那麼就會回收部分空閒(60秒不執行任務)的線程,當任務數增長時,此線程池又能夠智能的添加新線程來處理任務。此線程池不會對線程池大小作限制,線程池大小徹底依賴於操做系統(或者說JVM)可以建立的最大線程大小。 4.newScheduledThreadPool 建立一個大小無限的線程池。此線程池支持定時以及週期性執行任務的需求。 以上的全部底層實現都是用ThreadPoolExecutor這個來作的。具體的能夠查看源代碼 ================================================================================================================= Callable 與 Future 的應用 配合使用能夠獲得每一個線程的返回值 Callable返回 Future接收 ================================================================================================================= java.util.concurrent.Lock : 共同點:Lock可以完成synchronized所實現的全部功能 不一樣點:Lock有比synchronized更精確的線程和更好的性能,而且提供了多樣化的同步,好比有時間限制的同步,能夠被Interrupt的同步(synchronized的同步是不能Interrupt的)等。 synchronized是在JVM層面上實現的,在代碼執行時出現異常,JVM會自動釋放鎖定.但Lock不能,只能手動釋放,而且在finally從句中釋放   性能:在jdk1.5中Synchronized是使用的獨佔悲觀鎖,資源競爭不激烈的狀況下synchronized性能略高,資源競爭激烈的狀況下性能要遠低於Lock。         jdk1.6後synchronized和lock同樣都是使用CAS樂觀鎖操做,因此性能差很少,對於具體使用哪個要看具體的系統應用須要,Synchronized相對簡單易用,若須要精細的靈活控制則能夠考慮選擇lock。 Lock 也是採用compareAndSetState CAS 機制來實現鎖的 ReentrantLock 互斥鎖, 指的是一次最多隻能有一個線程持有的鎖. 在jdk1.5以前, 咱們一般使用synchronized機制控制多個線程對共享資源的訪問。而如今Lock提供了比synchronized機制更普遍靈活的鎖定操做。 ================================================================================================================= ReadWriteLock 讀寫鎖 ReentrantReadWriteLock 和 ReentrantLock 不是繼承關係,但都是基於 AbstractQueuedSynchronizer 來實現。 注意:在同一線程中,持有讀鎖後,不能直接調用寫鎖的lock方法 ,不然會形成死鎖。 讀 寫 讀 容許 不容許 寫 不容許 不容許 ================================================================================================================= Condition  能夠實現可阻塞隊列。可參考api裏的經典列子 Lock 替代了 synchronized 方法和語句的使用,Condition 替代了 Object 監視器方法的使用 Condition 實例實質上被綁定到一個鎖上。要爲特定 Lock 實例得到 Condition 實例,請使用其 newCondition() 方法。 class BoundedBuffer {    final Lock lock = new ReentrantLock();    final Condition notFull  = lock.newCondition();     final Condition notEmpty = lock.newCondition();     final Object[] items = new Object[100];    int putptr, takeptr, count;    public void put(Object x) throws InterruptedException {      lock.lock();      try {        while (count == items.length)           notFull.await();        items[putptr] = x;         if (++putptr == items.length) putptr = 0;        ++count;        notEmpty.signal();      } finally {        lock.unlock();      }    }    public Object take() throws InterruptedException {      lock.lock();      try {        while (count == 0)           notEmpty.await();        Object x = items[takeptr];         if (++takeptr == items.length) takeptr = 0;        --count;        notFull.signal();        return x;      } finally {        lock.unlock();      }    }   } ================================================================================================================= 自旋鎖 :相似於while死循環,不斷的判斷表達式,判斷是否還持有該把鎖對象.         會致使死鎖。若是長期持有自旋鎖 獨佔悲觀鎖:synchronized CAS樂觀鎖 :Lock
相關文章
相關標籤/搜索