22.JNInative是方法修飾符。Native方法是由另外一種語言(如c/c++,FORTRAN,彙編)實現的本地方法。
1)、繼承Thread類實現多線程
繼承Thread類的方法儘管被我列爲一種多線程實現方式,但Thread本質上也是實現了Runnable接口的一個實例,它代表一個線程的實例,並且,啓動線程的唯一方法就是通過Thread類的start()實例方法。start()方法是一個native方法,它將啓動一個新線程,並執行run()方法。這種方式實現多線程很簡單,通過自己的類直接extend
Thread,並複寫run()方法,就可以啓動新線程並執行自己定義的run()方法。例如
- ublic class MyThread extends Thread {
- public void run() {
- System.out.println("MyThread.run()");
- }
- }
- MyThread myThread1 = new MyThread();
- MyThread myThread2 = new MyThread();
- myThread1.start();
- myThread2.start();
- public class MyThread extends OtherClass implements Runnable {
- public void run() {
- System.out.println("MyThread.run()");
- }
- }
- MyThread myThread = new MyThread();
- Thread thread = new Thread(myThread);
- thread.start();
23.線程的狀態轉換
(一). 等待阻塞:運行(running)的線程執行o.wait()方法,JVM會把該線程放入等待隊列(waitting queue)中。由於notify()只是喚醒一個線程,但我們由不能確定具體喚醒的是哪一個線程,也許我們需要喚醒的線程不能夠被喚醒,因此在實際使用時,一般都用notifyAll()方法,喚醒有所線程)
(二). 同步阻塞:運行(running)的線程在獲取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入鎖池(lock pool)中。當線程剛進入可運行狀態(注意,還沒運行),發現將要調用的資源被synchroniza(同步),獲取不到鎖標記,將會立即進入鎖池狀態,
(三). 其他阻塞:運行(running)的線程執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O請求時,JVM會把該線程置爲阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入可運行(runnable)狀態。
線程同步方法:synchronize與wait-notifysynchronize與lock:自動與手動釋放鎖
sleep是線程類不會釋放鎖對象
wait是Object類方法會釋放鎖對象
24.如何停止一個線程
使用interrupt方法終止線程
(1)線程處於阻塞狀態,如使用了sleep方
法。
(2)使用while(!isInterrupted())
{……}來判斷線程是否被中斷。
在第一種情況下使用interrupt方法,sleep
方法將拋出一個InterruptedException例外,
而在第二種情況下線程將直接退出
1 )處於運行狀態的線程停止:
線程需要通過設置停止變量的方式
2)處於可中斷等待線程的停止:線程調用了sleep或wait方法,這些方法可拋出InterruptedException;則可以通過調用Thread的interrupt方法讓等待方法拋出InterruptedException異常,然後在循環外截獲並處理異常
3) 處於IO阻塞狀態線程的停止:線程調用了IO的read操作或者socket的accept操作,處於阻塞狀態。Java的InterruptableChanel接口提供了這樣的機制
25.如何保證線程安全
不要跨線程訪問共享變量
使共享變量是final類型的
將共享變量的操作加上同步
volatile聲明的數值類型變量進行運算, 往往是不安全的(volatile只能保證可見性,不能保證原子性
當一個線程請求獲得它自己佔有的鎖時(同一把鎖的嵌套使用), 我們稱該鎖爲可重入鎖
提供了可重入鎖的java實現-ReentrantLock. 每個共享變量,都應該由一個唯一確定的鎖保護.創建與變量相同數目的ReentrantLock,
使他們負責每個變量的線程安全.
使用ThreadLocal變量確保線程封閉性(封閉線程往往是比較安全的,
但一定程度上會造成性能損耗)封閉對象的例子在實際使用過程中
在併發編程中,
需要容器支持的時候, 優先考慮使用jdk併發容器(ConcurrentHashMap, ConcurrentLinkedQueue, CopyOnWriteArrayList.但是他們支持的併發實現並不一定意味着操作的原子性,他們只是保證數據結構不被破壞
http://blog.csdn.net/fp196391196391/article
/details/39493761
26.Synchronized如何使用
1)方法聲明時使用,放在範圍操作符(public等)之後,返回類型聲明(void等)之前.這時,線程獲得的是成員鎖,即一次只能有一個線程進入該方法,其他線程要想在此時調用該方法,只能排隊等候,當前線程(就是在synchronized方法內部的線程)執行完該方法後,別的線程才能進入.
public synchronized void synMethod() {
//方法體
}
2)對某一代碼塊使用,synchronized後跟括號,括號裏是變量,這樣,一次只有一個線程進入該代碼塊.此時,線程獲得的是成員鎖
public int synMethod(int a1){
synchronized(a1) {
//一次只能有一個線程進入
}
}
3)synchronized後面括號裏是一對象,此時,線程獲得的是對象鎖
public void run() {
synchronized (this) {
System.out.println(Thread.currentThread().getName());
}
4) synchronized後面括號裏是類,此時,線程獲得的是對象鎖
public ArrayWithLockOrder(int[] a)
{
arr = a;
synchronized(ArrayWithLockOrder.class) {//-----這裏
num_locks++; // 鎖數加 1。
lock_order = num_locks; // 爲此對象實例設置唯一的 lock_order。
}
27.
synchronized是在JVM層面上實現的,不但可以通過一些監控工具監控synchronized的鎖定,而且在代碼執行時出現異常,JVM會自動釋放鎖定,但是使用Lock則不行,lock是通過代碼實現的,要保證鎖定一定會被釋放,就必須將unLock()放到finally{}中
在資源競爭不是很激烈的情況下,Synchronized的性能要優於ReetrantLock,但是在資源競爭很激烈的情況下,Synchronized的性能會下降幾十倍,但是ReetrantLock的性能能維持常態;28.sleep和wait的區別
這兩個方法來自不同的類分別是,sleep來
自Thread類,和wait來自Object類。
最主要是sleep方法沒有釋放鎖,而wait方
法釋放了鎖,
、使用範圍:wait,notify和notifyAll只能
在同步控制方法或者同步控制塊裏面使用,
而sleep可以在任何地方使用
29.守護進程:
守護線程:守護線程則是用來服務用戶線程
的,如果沒有其他用戶線程在運行,那麼就
沒有可服務對象,也就沒有理由繼續下去。
setDaemon(boolean on)方法必須在線程
啓動之前調用,當線程正在運行時調用會產
生異常。
30.Java線程池技術及原理
使用線程池的好處:
1.減少在創建和銷燬線程上所花的時間以及系統資源的開銷
2.如不使用線程池,有可能造成系統創建大量線程而導致消耗完系統內存以及”過度切換”。
JDK自帶線程池總類
1)newFixedThreadPool創建一個指定工作線程數量的線程池。每當提交一個任務就創建一個工作線程,如果工作線程數量達到線程池初始的最大數,則將提交的任務存入到池隊列中。
2)newCachedThreadPool創建一個可緩存的線程池。工作線程的創建數量幾乎沒有限制(其實也有限制的,數目爲Interger. MAX_VALUE
3)newSingleThreadExecutor創建一個單線程化的Executor,即只創建唯一的工作者線程來執行任務,如果這個線程異常結束,會有另一個取代它,保證順序執行(我覺得這點是它的特色)
4)newScheduleThreadPool創建一個定長的線程池,
31.java併發包concurrent及常用的類
http://www.cnblogs.com/vijozsoft/p/558562
0.html//集合
Java內存模型:
java內存模型中規定了所有的變量都存儲在主內存中,每條線程還有自己的工作內存(可以與前面將的處理器的高速緩存類比),線程的工作內存中保存了該線程使用到的變量到主內存副本拷貝,線程對變量的所有操作(讀取、賦值)都必須在工作內存中進行,而不能直接讀寫主內存中的變量。不同線程之間無法直接訪問對方工作內存中的變量,線程間變量值的傳遞均需要在主內存來完成,線程、主內存和工作內存的交互關係如下圖所示
volatile關鍵字
-
可見性。對一個volatile變量的讀,總是能看到(任意線程)對這個volatile變量最後的寫入。
-
原子性:對任意單個volatile變量的讀/寫具有原子性,
下面對volatile寫和volatile讀的內存語義做個總結:
-
線程A寫一個volatile變量,實質上是線程A向接下來將要讀這個volatile變量的某個線程發出了(其對共享變量所在修改的)消息。
-
線程B讀一個volatile變量,實質上是線程B接收了之前某個線程發出的(在寫這個volatile變量之前對共享變量所做修改的)消息。
-
線程A寫一個volatile變量,隨後線程B讀這個volatile變量,這個過程實質上是線程A通過主內存向線程B發送消息。
1) ConcurrentHashMap
ConcurrentHashMap是線程安全的HashMap的實現,默認構造同樣有initialCapacity和loadFactor屬性,不過還多了一個concurrencyLevel屬性,三屬性默認值分別爲16、0.75及16。其內部使用鎖分段技術,維持這鎖Segment的數組,在Segment數組中又存放着Entity[]數組,內部hash算法將數據較均勻分佈在不同鎖中。
oncurrentHashMap默認情況下采用將數據分爲16個段進行存儲,並且16個段分別持有各自不同的鎖Segment,鎖僅用於put和remove等改變集合對象的操作,基於volatile及HashEntry鏈表的不變性實現了讀取的不加鎖。這些方式使得ConcurrentHashMap能夠保持極好的併發支持,尤其是對於讀遠比插入和刪除頻繁的Map而言,而它採用的這些方法也可謂是對於Java內存模型、併發機制深刻掌握的體現。
ConcurrentLinkedQueue是一個基於鏈接節點的、無界的、線程安全的隊列