Android 面試之 Java 篇三

本文出自 Eddy Wiki ,轉(zhuǎn)載請注明出處:http://eddy.wiki/interview-java.html

本文收集整理了 Android 面試中會遇到與 Java 知識相關(guān)的簡述題。

多線程

一個線程的生命周期

線程是一個動態(tài)執(zhí)行的過程,它也有一個從產(chǎn)生到死亡的過程。

下圖顯示了一個線程完整的生命周期。

img
  • 新建狀態(tài):使用 new 關(guān)鍵字和 Thread 類或其子類建立一個線程對象后,該線程對象就處于新建狀態(tài)。它保持這個狀態(tài)直到程序 start() 這個線程。
  • 就緒狀態(tài):當線程對象調(diào)用了start()方法之后,該線程就進入就緒狀態(tài)。就緒狀態(tài)的線程處于就緒隊列中,要等待JVM里線程調(diào)度器的調(diào)度。
  • 運行狀態(tài):如果就緒狀態(tài)的線程獲取 CPU 資源,就可以執(zhí)行 run(),此時線程便處于運行狀態(tài)。處于運行狀態(tài)的線程最為復(fù)雜,它可以變?yōu)樽枞麪顟B(tài)、就緒狀態(tài)和死亡狀態(tài)。
  • 阻塞狀態(tài):如果一個線程執(zhí)行了sleep(睡眠)、suspend(掛起)等方法,失去所占用資源之后,該線程就從運行狀態(tài)進入阻塞狀態(tài)。在睡眠時間已到或獲得設(shè)備資源后可以重新進入就緒狀態(tài)。可以分為三種: 等待阻塞:運行狀態(tài)中的線程執(zhí)行 wait() 方法,使線程進入到等待阻塞狀態(tài)。同步阻塞:線程在獲取 synchronized 同步鎖失敗(因為同步鎖被其他線程占用)。其他阻塞:通過調(diào)用線程的 sleep() 或 join() 發(fā)出了 I/O 請求時,線程就會進入到阻塞狀態(tài)。當sleep() 狀態(tài)超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程重新轉(zhuǎn)入就緒狀態(tài)。
  • **死亡狀態(tài): **一個運行狀態(tài)的線程完成任務(wù)或者其他終止條件發(fā)生時,該線程就切換到終止狀態(tài)。

Java 線程幾種用法

進程和線程的區(qū)別

程序是一段靜態(tài)的代碼。一個進程可以有一個或多個線程,每個線程都有一個唯一的標識符。

進程和線程的區(qū)別為:進程空間大體分為數(shù)據(jù)區(qū)、代碼區(qū)、棧區(qū)、堆區(qū)。多個進程的內(nèi)部數(shù)據(jù)和狀態(tài)都是完全獨立的;而線程共享進程的數(shù)據(jù)區(qū)、代碼區(qū)、堆區(qū),只有棧區(qū)是獨立的,所以線程切換比進程切換的代價小。

多線程技術(shù)是使得單個程序內(nèi)部可以在同一時刻執(zhí)行多個代碼段,完成不同的任務(wù),這種機制稱為多線程。同時并不是指真正意義上的同一時刻,而是指多個線程輪流占用CPU的時間片。


簡而言之,一個程序至少有一個進程,一個進程至少有一個線程。

線程的劃分尺度小于進程,使得多線程程序的并發(fā)性高。

另外,進程在執(zhí)行過程中擁有獨立的內(nèi)存單元,而多個線程共享內(nèi)存,從而極大地提高了程序的運行效率。

線程在執(zhí)行過程中與進程還是有區(qū)別的。每個獨立的線程有一個程序運行的入口、順序執(zhí)行序列和程序的出口。但是線程不能夠獨立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個線程執(zhí)行控制。

從邏輯角度來看,多線程的意義在于一個應(yīng)用程序中,有多個執(zhí)行部分可以同時執(zhí)行。但操作系統(tǒng)并沒有將多個線程看做多個獨立的應(yīng)用,來實現(xiàn)進程的調(diào)度和管理以及資源分配。這就是進程和線程的重要區(qū)別。

進程是具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)集合上的一次運行活動,進程是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位.

線程是進程的一個實體,是CPU調(diào)度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程自己基本上不擁有系統(tǒng)資源,只擁有一點在運行中必不可少的資源(如程序計數(shù)器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源.

一個線程可以創(chuàng)建和撤銷另一個線程;同一個進程中的多個線程之間可以并發(fā)執(zhí)行.

進程和線程的主要差別在于它們是不同的操作系統(tǒng)資源管理方式。進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產(chǎn)生影響,而線程只是一個進程中的不同執(zhí)行路徑。線程有自己的堆棧和局部變量,但線程之間沒有單獨的地址空間,一個線程死掉就等于整個進程死掉,所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對于一些要求同時進行并且又要共享某些變量的并發(fā)操作,只能用線程,不能用進程。

什么會導致線程阻塞

線程被堵塞可能是由下述五方面的原因造成的:

  1. 調(diào)用sleep(毫秒數(shù)),使線程進入“睡眠”狀態(tài)。在規(guī)定的時間內(nèi),這個線程是不會運行的。
  2. 用suspend()暫停了線程的執(zhí)行。除非線程收到resume()消息,否則不會返回“可運行”狀態(tài)。
  3. 用wait()暫停了線程的執(zhí)行。除非線程收到nofify()或者notifyAll()消息,否則不會變成“可運行”。
  4. 線程正在等候一些IO(輸入輸出)操作完成。
  5. 線程試圖調(diào)用另一個對象的“同步”方法,但那個對象處于鎖定狀態(tài),暫時無法使用。

啟動一個線程是用run()還是start()?

啟動一個線程是調(diào)用start()方法,使線程就緒狀態(tài),以后可以被調(diào)度為運行狀態(tài),一個線程必須關(guān)聯(lián)一些具體的執(zhí)行代碼,run()方法是該線程所關(guān)聯(lián)的執(zhí)行代碼。

多線程有幾種實現(xiàn)方法,都是什么?

Java 提供了三種創(chuàng)建線程的方法:

  • 通過實現(xiàn) Runable 接口;
  • 通過繼承 Thread類本身;
  • 通過 Callable 和 Future 創(chuàng)建線程。

多線程有兩種實現(xiàn)方法,分別是繼承Thread類與實現(xiàn)Runnable接口。

Java 5以前實現(xiàn)多線程有兩種實現(xiàn)方法:一種是繼承Thread類;另一種是實現(xiàn)Runnable接口。兩種方式都要通過重寫run()方法來定義線程的行為,推薦使用后者,因為Java中的繼承是單繼承,一個類有一個父類,如果繼承了Thread類就無法再繼承其他類了,顯然使用Runnable接口更為靈活。

實現(xiàn)Runnable接口相比繼承Thread類有如下優(yōu)勢:

  1. 可以避免由于Java的單繼承特性而帶來的局限
  2. 增強程序的健壯性,代碼能夠被多個程序共享,代碼與數(shù)據(jù)是獨立的
  3. 適合多個相同程序代碼的線程區(qū)處理同一資源的情況

補充:Java 5以后創(chuàng)建線程還有第三種方式:實現(xiàn)Callable接口,該接口中的call方法可以在線程執(zhí)行結(jié)束時產(chǎn)生一個返回值,代碼如下所示:

class MyTask implements Callable<Integer> {  
    private int upperBounds;  

    public MyTask(int upperBounds) {  
        this.upperBounds = upperBounds;  
    }  

    @Override  
    public Integer call() throws Exception {  
        int sum = 0;   
        for(int i = 1; i <= upperBounds; i++) {  
            sum += i;  
        }  
        return sum;  
    }  

}  

public class Test {  

    public static void main(String[] args) throws Exception {  
        List<Future<Integer>> list = new ArrayList<>();  
        ExecutorService service = Executors.newFixedThreadPool(10);  
        for(int i = 0; i < 10; i++) {  
            list.add(service.submit(new MyTask((int) (Math.random() * 100))));  
        }  

        int sum = 0;  
        for(Future<Integer> future : list) {  
            while(!future.isDone()) ;  
            sum += future.get();  
        }  

        System.out.println(sum);  
    }  
}  

同步有幾種實現(xiàn)方法,都是什么?

同步的實現(xiàn)方面有兩種,分別是synchronized、wait與notify

鎖的等級

方法鎖、對象鎖、類鎖

同步和異步的區(qū)別?

同步:A線程要請求某個資源,但是此資源正在被B線程使用中,因為同步機制存在,A線程請求不到,怎么辦,A線程只能等待下去

異步:A線程要請求某個資源,但是此資源正在被B線程使用中,因為沒有同步機制存在,A線程仍然請求的到,A線程無需等待


在進行網(wǎng)絡(luò)編程時,我們通常會看到同步、異步、阻塞、非阻塞四種調(diào)用方式以及他們的組合。

其中同步方式、異步方式主要是由客戶端(client)控制的,具體如下:

同步(Sync)

所謂同步,就是發(fā)出一個功能調(diào)用時,在沒有得到結(jié)果之前,該調(diào)用就不返回或繼續(xù)執(zhí)行后續(xù)操作。

異步(Async)

異步與同步相對,當一個異步過程調(diào)用發(fā)出后,調(diào)用者在沒有得到結(jié)果之前,就可以繼續(xù)執(zhí)行后續(xù)操作。當這個調(diào)用完成后,一般通過狀態(tài)、通知和回調(diào)來通知調(diào)用者。對于異步調(diào)用,調(diào)用的返回并不受調(diào)用者控制。

sleep和wait有什么區(qū)別?

一個是用來讓線程休息,一個是用來掛起線程

  • 對于sleep()方法,我們首先要知道該方法是屬于Thread類中的。而wait()方法,則是屬于Object類中的。
  • sleep():正在執(zhí)行的線程主動讓出CPU(然后CPU就可以去執(zhí)行其他任務(wù)),在sleep指定時間后CPU再回到該線程繼續(xù)往下執(zhí)行。注意:sleep方法只讓出了CPU,而并不會釋放同步資源鎖!

wait():則是指當前線程讓自己暫時退讓出同步資源鎖,以便其他正在等待該資源的線程得到該資源進而運行,只有調(diào)用了notify()方法,之前調(diào)用wait()的線程才會解除wait狀態(tài),可以去參與競爭同步資源鎖,進而得到執(zhí)行

sleep()方法是線程類(Thread)的靜態(tài)方法,導致此線程暫停執(zhí)行指定時間,將執(zhí)行機會給其他線程,但是監(jiān)控狀態(tài)依然保持,到時后會自動恢復(fù)(線程回到就緒(ready)狀態(tài)),因為調(diào)用sleep 不會釋放對象鎖。

wait()是Object 類的方法,對此對象調(diào)用wait()方法導致本線程放棄對象鎖(線程暫停執(zhí)行),進入等待此對象的等待鎖定池,只有針對此對象發(fā)出notify 方法(或notifyAll)后本線程才進入對象鎖定池準備獲得對象鎖進入就緒狀態(tài)。

Java線程池,線程同步

詳細講解一下 synchronized

在并發(fā)編程中,多線程同時并發(fā)訪問的資源叫做臨界資源,當多個線程同時訪問對象并要求操作相同資源時,分割了原子操作就有可能出現(xiàn)數(shù)據(jù)的不一致或數(shù)據(jù)不完整的情況,為避免這種情況的發(fā)生,我們會采取同步機制,以確保在某一時刻,方法內(nèi)只允許有一個線程。

采用synchronized修飾符實現(xiàn)的同步機制叫做互斥鎖機制,它所獲得的鎖叫做互斥鎖。每個對象都有一個monitor(鎖標記),當線程擁有這個鎖標記時才能訪問這個資源,沒有鎖標記便進入鎖池。任何一個對象系統(tǒng)都會為其創(chuàng)建一個互斥鎖,這個鎖是為了分配給線程的,防止打斷原子操作。每個對象的鎖只能分配給一個線程,因此叫做互斥鎖。

這里就使用同步機制獲取互斥鎖的情況,進行幾點說明:

  1. 如果同一個方法內(nèi)同時有兩個或更多線程,則每個線程有自己的局部變量拷貝。
  2. 類的每個實例都有自己的對象級別鎖。當一個線程訪問實例對象中的synchronized同步代碼塊或同步方法時,該線程便獲取了該實例的對象級別鎖,其他線程這時如果要訪問synchronized同步代碼塊或同步方法,便需要阻塞等待,直到前面的線程從同步代碼塊或方法中退出,釋放掉了該對象級別鎖。
  3. 訪問同一個類的不同實例對象中的同步代碼塊,不存在阻塞等待獲取對象鎖的問題,因為它們獲取的是各自實例的對象級別鎖,相互之間沒有影響。
  4. 持有一個對象級別鎖不會阻止該線程被交換出來,也不會阻塞其他線程訪問同一示例對象中的非synchronized代碼。當一個線程A持有一個對象級別鎖(即進入了synchronized修飾的代碼塊或方法中)時,線程也有可能被交換出去,此時線程B有可能獲取執(zhí)行該對象中代碼的時間,但它只能執(zhí)行非同步代碼(沒有用synchronized修飾),當執(zhí)行到同步代碼時,便會被阻塞,此時可能線程規(guī)劃器又讓A線程運行,A線程繼續(xù)持有對象級別鎖,當A線程退出同步代碼時(即釋放了對象級別鎖),如果B線程此時再運行,便會獲得該對象級別鎖,從而執(zhí)行synchronized中的代碼。
  5. 持有對象級別鎖的線程會讓其他線程阻塞在所有的synchronized代碼外。例如,在一個類中有三個synchronized方法a,b,c,當線程A正在執(zhí)行一個實例對象M中的方法a時,它便獲得了該對象級別鎖,那么其他的線程在執(zhí)行同一實例對象(即對象M)中的代碼時,便會在所有的synchronized方法處阻塞,即在方法a,b,c處都要被阻塞,等線程A釋放掉對象級別鎖時,其他的線程才可以去執(zhí)行方法a,b或者c中的代碼,從而獲得該對象級別鎖。
  6. 使用synchronized(obj)同步語句塊,可以獲取指定對象上的對象級別鎖。obj為對象的引用,如果獲取了obj對象上的對象級別鎖,在并發(fā)訪問obj對象時時,便會在其synchronized代碼處阻塞等待,直到獲取到該obj對象的對象級別鎖。當obj為this時,便是獲取當前對象的對象級別鎖。
  7. 類級別鎖被特定類的所有示例共享,它用于控制對static成員變量以及static方法的并發(fā)訪問。具體用法與對象級別鎖相似。
  8. 互斥是實現(xiàn)同步的一種手段,臨界區(qū)、互斥量和信號量都是主要的互斥實現(xiàn)方式。synchronized關(guān)鍵字經(jīng)過編譯后,會在同步塊的前后分別形成monitorenter和monitorexit這兩個字節(jié)碼指令。根據(jù)虛擬機規(guī)范的要求,在執(zhí)行monitorenter指令時,首先要嘗試獲取對象的鎖,如果獲得了鎖,把鎖的計數(shù)器加1,相應(yīng)地,在執(zhí)行monitorexit指令時會將鎖計數(shù)器減1,當計數(shù)器為0時,鎖便被釋放了。由于synchronized同步塊對同一個線程是可重入的,因此一個線程可以多次獲得同一個對象的互斥鎖,同樣,要釋放相應(yīng)次數(shù)的該互斥鎖,才能最終釋放掉該鎖。

當一個線程進入一個對象的一個synchronized方法后,其它線程是否可進入此對象的其它方法?

  • 可以調(diào)用此對象的其他非 synchronized 方法;
  • 可以調(diào)用此對象的 synchronized static 方法;
public class A {
     
    /**
     * 靜態(tài)方法
     */
    public synchronized static void staticMethod(){}
     
    /**
     * 實例方法
     */
    public synchronized void instanceMethod(){}
    public static void main(String[] args) {
         
        //A實例的創(chuàng)建過程
        Class c = Class.forName("A");
        A a1 = c.newInstance();
        A a2 = c.newInstance();
        A a3 = c.newInstance();
    }
}

如上代碼所示,你看一看main方法里面A實例的創(chuàng)建過程,這個要先理解staticMethod這個靜態(tài)方法,無法你實例化多少次,它都只是存在一個,就像Class c指向的對象,它在jvm中也只會存在一個,staticMethod方法鎖住的是c指向的實例。

instanceMethod這個實例方法,你創(chuàng)建多少個A實例,這些實例都存在各自的instanceMethod方法,這個方法前加synchronized關(guān)鍵詞,會鎖住該instanceMethod方法所在的實例。如a1的instanceMethod方法會鎖住a1指向的實例,a2的instanceMethod會鎖住a2指向的實例。

由此得出結(jié)論,staticMethod與instanceMethod鎖住的對象是不可能相同的,這就是兩個方法不能同步的原因。

內(nèi)存可見性

加鎖(synchronized同步)的功能不僅僅局限于互斥行為,同時還存在另外一個重要的方面:內(nèi)存可見性。我們不僅希望防止某個線程正在使用對象狀態(tài)而另一個線程在同時修改該狀態(tài),而且還希望確保當一個線程修改了對象狀態(tài)后,其他線程能夠看到該變化。而線程的同步恰恰也能夠?qū)崿F(xiàn)這一點。

內(nèi)置鎖可以用于確保某個線程以一種可預(yù)測的方式來查看另一個線程的執(zhí)行結(jié)果。為了確保所有的線程都能看到共享變量的最新值,可以在所有執(zhí)行讀操作或?qū)懖僮鞯木€程上加上同一把鎖。下圖示例了同步的可見性保證。

當線程A執(zhí)行某個同步代碼塊時,線程B隨后進入由同一個鎖保護的同步代碼塊,這種情況下可以保證,當鎖被釋放前,A看到的所有變量值(鎖釋放前,A看到的變量包括y和x)在B獲得同一個鎖后同樣可以由B看到。換句話說,當線程B執(zhí)行由鎖保護的同步代碼塊時,可以看到線程A之前在同一個鎖保護的同步代碼塊中的所有操作結(jié)果。如果在線程A unlock M之后,線程B才進入lock M,那么線程B都可以看到線程A unlock M之前的操作,可以得到i=1,j=1。如果在線程B unlock M之后,線程A才進入lock M,那么線程B就不一定能看到線程A中的操作,因此j的值就不一定是1。

現(xiàn)在考慮如下代碼:

public class  MutableInteger  
{  
    private int value;  

    public int get(){  
        return value;  
    }  
    public void set(int value){  
        this.value = value;  
    }  
}  

以上代碼中,get和set方法都在沒有同步的情況下訪問value。如果value被多個線程共享,假如某個線程調(diào)用了set,那么另一個正在調(diào)用get的線程可能會看到更新后的value值,也可能看不到。

通過對set和get方法進行同步,可以使MutableInteger成為一個線程安全的類,如下:

public class  SynchronizedInteger  
{  
    private int value;  

    public synchronized int get(){  
        return value;  
    }  
    public synchronized void set(int value){  
        this.value = value;  
    }  
}  

對set和get方法進行了同步,加上了同一把對象鎖,這樣get方法可以看到set方法中value值的變化,從而每次通過get方法取得的value的值都是最新的value值。

Atomic、volatile、synchronized區(qū)別

面Java基礎(chǔ)的時候遇上了這個問題,說如果只有一個i++;的時候,volatilesynchronized能否互換。當時也不知道,感覺volatile作為修飾變量的時候,變量自加會出現(xiàn)加到一半發(fā)生線程調(diào)度。再看看當時蒙對了。

volatile
可以保證在一個線程的工作內(nèi)存中修改了該變量的值,該變量的值立即能回顯到主內(nèi)存中,從而保證所有的線程看到這個變量的值是一致的。但是有個前提,因為它
不具有操作的原子性,也就是它不適合在對該變量的寫操作依賴于變量本身自己。就比如i++、i+=1;這種。但是可以改為num=i+1;如果i是一個 volatile 類型,那么num就是安全的,總之就是不能作用于自身。

synchronized是基于代碼塊的,只要包含在synchronized塊中,就是線程安全的。

既然都說了線程安全,就多了解幾個:

AtomicInteger,一個輕量級的synchronized。使用的并不是同步代碼塊,而是Lock-Free算法(我也不懂,看代碼就是一個死循環(huán)調(diào)用了底層的比較方法直到相同后才退出循環(huán))。最終的結(jié)果就是在高并發(fā)的時候,或者說競爭激烈的時候效率比synchronized高一些。

ThreadLocal,線程中私有數(shù)據(jù)。主要用于線程改變內(nèi)部的數(shù)據(jù)時不影響其他線程,使用時需要注意static

詳細分析見這篇文章

再補一個,才學到的。利用clone()方法,如果是一個類的多個對象想共用對象內(nèi)部的一個變量,而又不想這個變量static,可以使用淺復(fù)制方式。(查看設(shè)計模式原型模式)

并發(fā)編程中實現(xiàn)內(nèi)存可見的兩種方法比較:加鎖和volatile變量

  1. volatile變量是一種稍弱的同步機制在訪問volatile變量時不會執(zhí)行加鎖操作,因此也就不會使執(zhí)行線程阻塞,因此volatile變量是一種比synchronized關(guān)鍵字更輕量級的同步機制。
  1. 從內(nèi)存可見性的角度看,寫入volatile變量相當于退出同步代碼塊,而讀取volatile變量相當于進入同步代碼塊。
  2. 在代碼中如果過度依賴volatile變量來控制狀態(tài)的可見性,通常會比使用鎖的代碼更脆弱,也更難以理解。僅當volatile變量能簡化代碼的實現(xiàn)以及對同步策略的驗證時,才應(yīng)該使用它。一般來說,用同步機制會更安全些。
  3. 加鎖機制(即同步機制)既可以確保可見性又可以確保原子性,而volatile變量只能確保可見性,原因是聲明為volatile的簡單變量如果當前值與該變量以前的值相關(guān),那么volatile關(guān)鍵字不起作用,也就是說如下的表達式都不是原子操作:“count++”、“count = count+1”。

當且僅當滿足以下所有條件時,才應(yīng)該使用volatile變量:

  1. 對變量的寫入操作不依賴變量的當前值,或者你能確保只有單個線程更新變量的值。
  2. 該變量沒有包含在具有其他變量的不變式中。

總結(jié):在需要同步的時候,第一選擇應(yīng)該是synchronized關(guān)鍵字,這是最安全的方式,嘗試其他任何方式都是有風險的。尤其在、jdK1.5之后,對synchronized同步機制做了很多優(yōu)化,如:自適應(yīng)的自旋鎖、鎖粗化、鎖消除、輕量級鎖等,使得它的性能明顯有了很大的提升。

用wait/notify來實現(xiàn)最簡單的生產(chǎn)者-消費者模式

生產(chǎn)者和消費者問題

守護線程與阻塞線程的四種情況

Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護線程)

用戶線程即運行在前臺的線程,而守護線程是運行在后臺的線程。 守護線程作用是為其他前臺線程的運行提供便利服務(wù),而且僅在普通、非守護線程仍然運行時才需要,比如垃圾回收線程就是一個守護線程。當VM檢測僅剩一個守護線程,而用戶線程都已經(jīng)退出運行時,VM就會退出,因為如果沒有了守護者,也就沒有繼續(xù)運行程序的必要了。如果有非守護線程仍然活著,VM就不會退出。

守護線程并非只有虛擬機內(nèi)部提供,用戶在編寫程序時也可以自己設(shè)置守護線程。用戶可以用Thread的setDaemon(true)方法設(shè)置當前線程為守護線程。

雖然守護線程可能非常有用,但必須小心確保其它所有非守護線程消亡時,不會由于它的終止而產(chǎn)生任何危害。因為你不可能知道在所有的用戶線程退出運行前,守護線程是否已經(jīng)完成了預(yù)期的服務(wù)任務(wù)。一旦所有的用戶線程退出了,虛擬機也就退出運行了。因此,不要再守護線程中執(zhí)行業(yè)務(wù)邏輯操作(比如對數(shù)據(jù)的讀寫等)。

還有幾點:

  1. setDaemon(true)必須在調(diào)用線程的start()方法之前設(shè)置,否則會跑出IllegalThreadStateException異常。
  2. 在守護線程中產(chǎn)生的新線程也是守護線程
  3. 不要認為所有的應(yīng)用都可以分配給守護線程來進行服務(wù),比如讀寫操作或者計算邏輯。

死鎖

當線程需要同時持有多個鎖時,有可能產(chǎn)生死鎖。考慮如下情形:

線程A當前持有互斥所鎖lock1,線程B當前持有互斥鎖lock2。接下來,當線程A仍然持有l(wèi)ock1時,它試圖獲取lock2,因為線程B正持有l(wèi)ock2,因此線程A會阻塞等待線程B對lock2的釋放。如果此時線程B在持有l(wèi)ock2的時候,也在試圖獲取lock1,因為線程A正持有l(wèi)ock1,因此線程B會阻塞等待A對lock1的釋放。二者都在等待對方所持有鎖的釋放,而二者卻又都沒釋放自己所持有的鎖,這時二者便會一直阻塞下去。這種情形稱為死鎖。

下面給出一個兩個線程間產(chǎn)生死鎖的示例,如下:

public class Deadlock {

    private String objID;

    public Deadlock(String id) {
        objID = id;
    }

    public synchronized void checkOther(Deadlock other) {
        print("entering checkOther()");  
        try { Thread.sleep(2000); }   
        catch ( InterruptedException x ) { }  
        print("in checkOther() - about to " + "invoke 'other.action()'");  
      //調(diào)用other對象的action方法,由于該方法是同步方法,因此會試圖獲取other對象的對象鎖  
        other.action();  
        print("leaving checkOther()");  
    }

    public synchronized void action() {  
        print("entering action()");  
        try { Thread.sleep(500); }   
        catch ( InterruptedException x ) { }  
        print("leaving action()");  
    }  

    public void print(String msg) {
        threadPrint("objID=" + objID + " - " + msg);
    }

    public static void threadPrint(String msg) {  
        String threadName = Thread.currentThread().getName();  
        System.out.println(threadName + ": " + msg);  
    }  

    public static void main(String[] args) {
        final Deadlock obj1 = new Deadlock("obj1");  
        final Deadlock obj2 = new Deadlock("obj2");  

        Runnable runA = new Runnable() {  
            public void run() {  
                obj1.checkOther(obj2);  
            }  
        };  

        Thread threadA = new Thread(runA, "threadA");  
        threadA.start();  

        try { Thread.sleep(200); }   
        catch ( InterruptedException x ) { }  

        Runnable runB = new Runnable() {  
            public void run() {  
                obj2.checkOther(obj1);  
            }  
        };  

        Thread threadB = new Thread(runB, "threadB");  
        threadB.start();  

        try { Thread.sleep(5000); }   
        catch ( InterruptedException x ) { }  

        threadPrint("finished sleeping");  

        threadPrint("about to interrupt() threadA"); 

        threadA.interrupt();  

        try { Thread.sleep(1000); }   
        catch ( InterruptedException x ) { }  

        threadPrint("about to interrupt() threadB");  
        threadB.interrupt();  

        try { Thread.sleep(1000); }   
        catch ( InterruptedException x ) { }  

        threadPrint("did that break the deadlock?");  
    }
}

運行結(jié)果:

threadA: objID=obj1 - entering checkOther()
threadB: objID=obj2 - entering checkOther()
threadA: objID=obj1 - in checkOther() - about to invoke 'other.action()'
threadB: objID=obj2 - in checkOther() - about to invoke 'other.action()'
main: finished sleeping
main: about to interrupt() threadA
main: about to interrupt() threadB
main: did that break the deadlock?

從結(jié)果中可以看出,在執(zhí)行到other.action()時,由于兩個線程都在試圖獲取對方的鎖,但對方都沒有釋放自己的鎖,因而便產(chǎn)生了死鎖,在主線程中試圖中斷兩個線程,但都無果。

大部分代碼并不容易產(chǎn)生死鎖,死鎖可能在代碼中隱藏相當長的時間,等待不常見的條件地發(fā)生,但即使是很小的概率,一旦發(fā)生,便可能造成毀滅性的破壞。避免死鎖是一件困難的事,遵循以下原則有助于規(guī)避死鎖:

  1. 只在必要的最短時間內(nèi)持有鎖,考慮使用同步語句塊代替整個同步方法;
  2. 盡量編寫不在同一時刻需要持有多個鎖的代碼,如果不可避免,則確保線程持有第二個鎖的時間盡量短暫;
  3. 創(chuàng)建和使用一個大鎖來代替若干小鎖,并把這個鎖用于互斥,而不是用作單個對象的對象級別鎖;

可重入內(nèi)置鎖

每個Java對象都可以用做一個實現(xiàn)同步的鎖,這些鎖被稱為內(nèi)置鎖或監(jiān)視器鎖。線程在進入同步代碼塊之前會自動獲取鎖,并且在退出同步代碼塊時會自動釋放鎖。獲得內(nèi)置鎖的唯一途徑就是進入由這個鎖保護的同步代碼塊或方法。

當某個線程請求一個由其他線程持有的鎖時,發(fā)出請求的線程就會阻塞。然而,由于內(nèi)置鎖是可重入的,因此如果摸個線程試圖獲得一個已經(jīng)由它自己持有的鎖,那么這個請求就會成功。“重入”意味著獲取鎖的操作的粒度是“線程”,而不是調(diào)用。重入的一種實現(xiàn)方法是,為每個鎖關(guān)聯(lián)一個獲取計數(shù)值和一個所有者線程。當計數(shù)值為0時,這個鎖就被認為是沒有被任何線程所持有,當線程請求一個未被持有的鎖時,JVM將記下鎖的持有者,并且將獲取計數(shù)值置為1,如果同一個線程再次獲取這個鎖,計數(shù)值將遞增,而當線程退出同步代碼塊時,計數(shù)器會相應(yīng)地遞減。當計數(shù)值為0時,這個鎖將被釋放。

重入進一步提升了加鎖行為的封裝性,因此簡化了面向?qū)ο蟛l(fā)代碼的開發(fā)。分析如下程序:

public class Father  
{  
    public synchronized void doSomething(){  
        ......  
    }  
}  

public class Child extends Father  
{  
    public synchronized void doSomething(){  
        ......  
        super.doSomething();  
    }  
}  

子類覆寫了父類的同步方法,然后調(diào)用父類中的方法,此時如果沒有可重入的鎖,那么這段代碼件產(chǎn)生死鎖。

由于Fither和Child中的doSomething方法都是synchronized方法,因此每個doSomething方法在執(zhí)行前都會獲取Child對象實例上的鎖。如果內(nèi)置鎖不是可重入的,那么在調(diào)用super.doSomething時將無法獲得該Child對象上的互斥鎖,因為這個鎖已經(jīng)被持有,從而線程會永遠阻塞下去,一直在等待一個永遠也無法獲取的鎖。重入則避免了這種死鎖情況的發(fā)生。

同一個線程在調(diào)用本類中其他synchronized方法/塊或父類中的synchronized方法/塊時,都不會阻礙該線程地執(zhí)行,因為互斥鎖時可重入的。

使用wait/notify/notifyAll實現(xiàn)線程間通信

在Java中,可以通過配合調(diào)用Object對象的wait()方法和notify()方法或notifyAll()方法來實現(xiàn)線程間的通信。在線程中調(diào)用wait()方法,將阻塞等待其他線程的通知(其他線程調(diào)用notify()方法或notifyAll()方法),在線程中調(diào)用notify()方法或notifyAll()方法,將通知其他線程從wait()方法處返回。

Object是所有類的超類,它有5個方法組成了等待/通知機制的核心:notify()、notifyAll()、wait()、wait(long)和wait(long,int)。在Java中,所有的類都從Object繼承而來,因此,所有的類都擁有這些共有方法可供使用。而且,由于他們都被聲明為final,因此在子類中不能覆寫任何一個方法。

這里詳細說明一下各個方法在使用中需要注意的幾點:

1、wait()

public final void wait()  throws InterruptedException,IllegalMonitorStateException

該方法用來將當前線程置入休眠狀態(tài),直到接到通知或被中斷為止。在調(diào)用wait()之前,線程必須要獲得該對象的對象級別鎖,即只能在同步方法或同步塊中調(diào)用wait()方法。進入wait()方法后,當前線程釋放鎖。在從wait()返回前,線程與其他線程競爭重新獲得鎖。如果調(diào)用wait()時,沒有持有適當?shù)逆i,則拋出IllegalMonitorStateException,它是RuntimeException的一個子類,因此,不需要try-catch結(jié)構(gòu)。

2、notify()

public final native void notify() throws IllegalMonitorStateException

該方法也要在同步方法或同步塊中調(diào)用,即在調(diào)用前,線程也必須要獲得該對象的對象級別鎖,的如果調(diào)用notify()時沒有持有適當?shù)逆i,也會拋出IllegalMonitorStateException。

該方法用來通知那些可能等待該對象的對象鎖的其他線程。如果有多個線程等待,則線程規(guī)劃器任意挑選出其中一個wait()狀態(tài)的線程來發(fā)出通知,并使它等待獲取該對象的對象鎖(notify后,當前線程不會馬上釋放該對象鎖,wait所在的線程并不能馬上獲取該對象鎖,要等到程序退出synchronized代碼塊后,當前線程才會釋放鎖,wait所在的線程也才可以獲取該對象鎖),但不驚動其他同樣在等待被該對象notify的線程們。當?shù)谝粋€獲得了該對象鎖的wait線程運行完畢以后,它會釋放掉該對象鎖,此時如果該對象沒有再次使用notify語句,則即便該對象已經(jīng)空閑,其他wait狀態(tài)等待的線程由于沒有得到該對象的通知,會繼續(xù)阻塞在wait狀態(tài),直到這個對象發(fā)出一個notify或notifyAll。這里需要注意:它們等待的是被notify或notifyAll,而不是鎖。這與下面的notifyAll()方法執(zhí)行后的情況不同。

3、notifyAll()

public final native void notifyAll() throws IllegalMonitorStateException

該方法與notify()方法的工作方式相同,重要的一點差異是:

notifyAll使所有原來在該對象上wait的線程統(tǒng)統(tǒng)退出wait的狀態(tài)(即全部被喚醒,不再等待notify或notifyAll,但由于此時還沒有獲取到該對象鎖,因此還不能繼續(xù)往下執(zhí)行),變成等待獲取該對象上的鎖,一旦該對象鎖被釋放(notifyAll線程退出調(diào)用了notifyAll的synchronized代碼塊的時候),他們就會去競爭。如果其中一個線程獲得了該對象鎖,它就會繼續(xù)往下執(zhí)行,在它退出synchronized代碼塊,釋放鎖后,其他的已經(jīng)被喚醒的線程將會繼續(xù)競爭獲取該鎖,一直進行下去,直到所有被喚醒的線程都執(zhí)行完畢。

4、wait(long)和wait(long,int)

顯然,這兩個方法是設(shè)置等待超時時間的,后者在超值時間上加上ns,精度也難以達到,因此,該方法很少使用。對于前者,如果在等待線程接到通知或被中斷之前,已經(jīng)超過了指定的毫秒數(shù),則它通過競爭重新獲得鎖,并從wait(long)返回。另外,需要知道,如果設(shè)置了超時時間,當wait()返回時,我們不能確定它是因為接到了通知還是因為超時而返回的,因為wait()方法不會返回任何相關(guān)的信息。但一般可以通過設(shè)置標志位來判斷,在notify之前改變標志位的值,在wait()方法后讀取該標志位的值來判斷,當然為了保證notify不被遺漏,我們還需要另外一個標志位來循環(huán)判斷是否調(diào)用wait()方法。

深入理解:

  • 如果線程調(diào)用了對象的wait()方法,那么線程便會處于該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。
  • 當有線程調(diào)用了對象的notifyAll()方法(喚醒所有wait線程)或notify()方法(只隨機喚醒一個wait線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。
  • 優(yōu)先級高的線程競爭到對象鎖的概率大,假若某線程沒有競爭到該對象鎖,它還會留在鎖池中,唯有線程再次調(diào)用wait()方法,它才會重新回到等待池中。而競爭到對象鎖的線程則繼續(xù)往下執(zhí)行,直到執(zhí)行完了synchronized代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續(xù)競爭該對象鎖。

線程中斷

參考:

線程中斷

Error 和 Exception

Error與Exception的區(qū)別

Error是Throwable的子類,用于標記嚴重錯誤。合理的應(yīng)用程序不應(yīng)該去try/catch這種錯誤。絕大多數(shù)的錯誤都是非正常的,就根本不該出現(xiàn)的。

Exception 是Throwable的一種形式的子類,用于指示一種合理的程序想去catch的條件。即它僅僅是一種程序運行條件,而非嚴重錯誤,并且鼓勵用戶程序去catch它。

checked exceptions: 通常是從一個可以恢復(fù)的程序中拋出來的,并且最好能夠從這種異常中使用程序恢復(fù)。比如FileNotFoundException, ParseException等。

unchecked exceptions: 通常是如果一切正常的話本不該發(fā)生的異常,但是的確發(fā)生了。比如ArrayIndexOutOfBoundException, ClassCastException等。從語言本身的角度講,程序不該去catch這類異常,雖然能夠從諸如RuntimeException這樣的異常中catch并恢復(fù),但是并不鼓勵終端程序員這么做,因為完全沒要必要。因為這類錯誤本身就是bug,應(yīng)該被修復(fù),出現(xiàn)此類錯誤時程序就應(yīng)該立即停止執(zhí)行。 因此,面對Errors和unchecked exceptions應(yīng)該讓程序自動終止執(zhí)行,程序員不該做諸如try/catch這樣的事情,而是應(yīng)該查明原因,修改代碼邏輯。

Java中的異常處理機制的簡單原理和應(yīng)用。

JAVA語言如何進行異常處理,關(guān)鍵字:throws,throw,try,catch,finally分別代表什么意義?在try塊中可以拋出異常嗎?

Java通過面向?qū)ο蟮姆椒ㄟM行異常處理,把各種不同的異常進行分類,并提供了良好的接口。在Java中,每個異常都是一個對象,它是Throwable類或其它子類的實例。當一個方法出現(xiàn)異常后便拋出一個異常對象,該對象中包含有異常信息,調(diào)用這個對象的方法可以捕獲到這個異常并進行處理。Java的異常處理是通過5個關(guān)鍵詞來實現(xiàn)的:try、catch、throw、throws和finally。一般情況下是用try來執(zhí)行一段程序,如果出現(xiàn)異常,系統(tǒng)會拋出(throws)一個異常,這時候你可以通過它的類型來捕捉(catch)它,或最后(finally)由缺省處理器來處理。用try來指定一塊預(yù)防所有“異常”的程序。緊跟在try程序后面,應(yīng)包含一個catch子句來指定你想要捕捉的“異常”的類型。throw語句用來明確地拋出一個“異常”。throws用來標明一個成員函數(shù)可能拋出的各種“異常”。Finally為確保一段代碼不管發(fā)生什么“異常”都被執(zhí)行一段代碼。可以在一個成員函數(shù)調(diào)用的外面寫一個try語句,在這個成員函數(shù)內(nèi)部寫另一個try語句保護其他代碼。每當遇到一個try語句,“異常”的框架就放到堆棧上面,直到所有的try語句都完成。如果下一級的try語句沒有對某種“異常”進行處理,堆棧就會展開,直到遇到有處理這種“異常”的try語句。

try{ return} catch{} finally{}; return 還是 finally 先執(zhí)行。

任何執(zhí)行try 或者catch中的return語句之前,都會先執(zhí)行finally語句,如果finally存在的話。

如果finally中有return語句,那么程序就return了,所以finally中的return是一定會被return的。

在try語句中,在執(zhí)行return語句時,要返回的結(jié)果已經(jīng)準備好了,就在此時,程序轉(zhuǎn)到finally執(zhí)行了。在轉(zhuǎn)去之前,try中先把要返回的結(jié)果存放到不同于x的局部變量中去,執(zhí)行完finally之后,在從中取出返回結(jié)果,因此,即使finally中對變量x進行了改變,但是不會影響返回結(jié)果。

序列化

對象Object讀寫的是哪兩個流

ObjectInputStream

ObjectOutputStream

什么是Java序列化,如何實現(xiàn)java序列化

序列化就是一種用來處理對象流的機制,所謂對象流也就是將對象的內(nèi)容進行流化。可以對流化后的對象進行讀寫操作,也可將流化后的對象傳輸于網(wǎng)絡(luò)之間。序列化是為了解決在對對象流進行讀寫操作時所引發(fā)的問題。

序列化的實現(xiàn):將需要被序列化的類實現(xiàn)Serializable接口,該接口沒有需要實現(xiàn)的方法,implements Serializable只是為了標注該對象是可被序列化的,然后使用一個輸出流(如:FileOutputStream)來構(gòu)造一個ObjectOutputStream(對象流)對象,接著,使用ObjectOutputStream對象的writeObject(Object obj)方法就可以將參數(shù)為obj的對象寫出(即保存其狀態(tài)),要恢復(fù)的話則用輸入流。

Serializable和Parcelable

注解

注解原理

反射

反射原理

反射機制

java反射機制是在運行狀態(tài)中,對于任意一個類, 都能夠知道這個類的所有屬性和方法;對于任意一個對象, 都能夠調(diào)用它的任意一個方法和屬性;這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為java語言的反射機制。

主要作用有三:

  1. 運行時取得類的方法和字段的相關(guān)信息。
  2. 創(chuàng)建某個類的新實例(.newInstance())
  3. 取得字段引用直接獲取和設(shè)置對象字段,無論訪問修飾符是什么。

用處如下:

  1. 觀察或操作應(yīng)用程序的運行時行為。
  2. 調(diào)試或測試程序,因為可以直接訪問方法、構(gòu)造函數(shù)和成員字段。
  3. 通過名字調(diào)用不知道的方法并使用該信息來創(chuàng)建對象和調(diào)用方法。

Java類加載機制

泛型

泛型的優(yōu)缺點

優(yōu)點:

使用泛型類型可以最大限度地重用代碼、保護類型的安全以及提高性能。泛型最常見的用途是創(chuàng)建集合類。

缺點:

在性能上不如數(shù)組快。

泛型常用特點,List<String>能否轉(zhuǎn)為List<Object>

能,但是利用類都繼承自O(shè)bject,所以使用是每次調(diào)用里面的函數(shù)都要通過強制轉(zhuǎn)換還原回原來的類,這樣既不安全,運行速度也慢。

網(wǎng)絡(luò)

TCP與UDP的區(qū)別

TCP UDP
TCP面向有鏈接的通信服務(wù) UDP面向無連接的通信服務(wù)
TCP提供可靠的通信傳輸 UDP不可靠,會丟包
TCP保證數(shù)據(jù)順序 UDP不保證
TCP數(shù)據(jù)無邊界 UDP有邊界
TCP速度慢 UDP速度快
TCP面向字節(jié)流 UDP面向報文
TCP一對一 UDP可以一對一,一對多
TCP報頭至少20字節(jié) UDP報頭8字節(jié)
TCP有流量控制,擁塞控制 UDP沒有

http get 和 post 的區(qū)別

  • GET請求的數(shù)據(jù)會附在URL之后(就是把數(shù)據(jù)放置在HTTP協(xié)議頭中),以?分割URL和傳輸數(shù)據(jù),參數(shù)之間以&相連,如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0%E5%A5%BD。如果數(shù)據(jù)是英文字母/數(shù)字,原樣發(fā)送,如果是空格,轉(zhuǎn)換為+,如果是中文/其他字符,則直接把字符串用BASE64加密,得出如:%E4%BD%A0%E5%A5%BD,其中%XX中的XX為該符號以16進制表示的ASCII。

POST把提交的數(shù)據(jù)則放置在是HTTP包的包體中。

  • GET方式提交的數(shù)據(jù)最多只能是1024字節(jié),理論上POST沒有限制,可傳較大量的數(shù)據(jù)。

首先是"GET方式提交的數(shù)據(jù)最多只能是1024字節(jié)",因為GET是通過URL提交數(shù)據(jù),那么GET可提交的數(shù)據(jù)量就跟URL的長度有直接關(guān)系了。而實際上,URL不存在參數(shù)上限的問題,HTTP協(xié)議規(guī)范沒有對URL長度進行限制。這個限制是特定的瀏覽器及服務(wù)器對它的限制。IE對URL長度的限制是2083字節(jié)(2K+35)。對于其他瀏覽器,如Netscape、FireFox等,理論上沒有長度限制,其限制取決于操作系統(tǒng)的支持。

注意這是限制是整個URL長度,而不僅僅是你的參數(shù)值數(shù)據(jù)長度。

  • POST的安全性要比GET的安全性高。注意:這里所說的安全性和上面GET提到的“安全”不是同個概念。上面“安全”的含義僅僅是不作數(shù)據(jù)修改,而這里安全的含義是真正的Security的含義,比如:通過GET提交數(shù)據(jù),用戶名和密碼將明文出現(xiàn)在URL上,因為(1)登錄頁面有可能被瀏覽器緩存,(2)其他人查看瀏覽器的歷史紀錄,那么別人就可以拿到你的賬號和密碼了。

GET - 從指定的服務(wù)器中獲取數(shù)據(jù)
POST - 提交數(shù)據(jù)給指定的服務(wù)器處理

GET方法:
使用GET方法時,查詢字符串(鍵值對)被附加在URL地址后面一起發(fā)送到服務(wù)器:
/test/demo_form.jsp?name1=value1&name2=value2
特點:

GET請求能夠被緩存
GET請求會保存在瀏覽器的瀏覽記錄中
以GET請求的URL能夠保存為瀏覽器書簽
GET請求有長度限制
GET請求主要用以獲取數(shù)據(jù)

POST方法:
使用POST方法時,查詢字符串在POST信息中單獨存在,和HTTP請求一起發(fā)送到服務(wù)器:
POST /test/demo_form.jsp HTTP/1.1
Host: w3schools.com
name1=value1&name2=value2
特點:

POST請求不能被緩存下來
POST請求不會保存在瀏覽器瀏覽記錄中
以POST請求的URL無法保存為瀏覽器書簽
POST請求沒有長度限制
Post一般用于更新或者添加資源信息 Get一般用于查詢操作,而且應(yīng)該是安全和冪等的
Post更加安全 Get會把請求的信息放到URL的后面
Post傳輸量一般無大小限制 Get不能大于2KB
Post執(zhí)行效率低 Get執(zhí)行效率略高

https的那個s是什么意思

UDP 和 TCP 的區(qū)別

  1. 基于連接與無連接;
  2. 對系統(tǒng)資源的要求(TCP較多,UDP少);
  3. UDP程序結(jié)構(gòu)較簡單;
  4. 流模式與數(shù)據(jù)包模式 ;
  5. TCP保證數(shù)據(jù)正確性,UDP可能丟包,TCP保證數(shù)據(jù)順序,UDP不保證。

TCP:面向連接、傳輸可靠(保證數(shù)據(jù)正確性,保證數(shù)據(jù)順序)、用于傳輸大量數(shù)據(jù)(流模式)、速度慢,建立連接需要開銷較多(時間,系統(tǒng)資源)。

UDP:面向非連接、傳輸不可靠、用于傳輸少量數(shù)據(jù)(數(shù)據(jù)包模式)、速度快。


TCP是Tranfer Control Protocol的 簡稱,是一種面向連接的保證可靠傳輸?shù)膮f(xié)議。通過TCP協(xié)議傳輸,得到的是一個順序的無差錯的數(shù)據(jù)流。發(fā)送方和接收方的成對的兩個socket之間必須建 立連接,以便在TCP協(xié)議的基礎(chǔ)上進行通信,當一個socket(通常都是server socket)等待建立連接時,另一個socket可以要求進行連接,一旦這兩個socket連接起來,它們就可以進行雙向數(shù)據(jù)傳輸,雙方都可以進行發(fā)送 或接收操作。

UDP是User Datagram Protocol的簡稱,是一種無連接的協(xié)議,每個數(shù)據(jù)報都是一個獨立的信息,包括完整的源地址或目的地址,它在網(wǎng)絡(luò)上以任何可能的路徑傳往目的地,因此能否到達目的地,到達目的地的時間以及內(nèi)容的正確性都是不能被保證的。

比較:

UDP:

1,每個數(shù)據(jù)報中都給出了完整的地址信息,因此無需要建立發(fā)送方和接收方的連接。

2,UDP傳輸數(shù)據(jù)時是有大小限制的,每個被傳輸?shù)臄?shù)據(jù)報必須限定在64KB之內(nèi)。

3,UDP是一個不可靠的協(xié)議,發(fā)送方所發(fā)送的數(shù)據(jù)報并不一定以相同的次序到達接收方

TCP:

1,面向連接的協(xié)議,在socket之間進行數(shù)據(jù)傳輸之前必然要建立連接,所以在TCP中需要連接時間。

2,TCP傳輸數(shù)據(jù)大小限制,一旦連接建立起來,雙方的socket就可以按統(tǒng)一的格式傳輸大的數(shù)據(jù)。

3,TCP是一個可靠的協(xié)議,它確保接收方完全正確地獲取發(fā)送方所發(fā)送的全部數(shù)據(jù)。

應(yīng)用:

1,TCP在網(wǎng)絡(luò)通信上有極強的生命力,例如遠程連接(Telnet)和文件傳輸(FTP)都需要不定長度的數(shù)據(jù)被可靠地傳輸。但是可靠的傳輸是要付出代價的,對數(shù)據(jù)內(nèi)容正確性的檢驗必然占用計算機的處理時間和網(wǎng)絡(luò)的帶寬,因此TCP傳輸?shù)男什蝗鏤DP高。

2,UDP操作簡單,而且僅需要較少的監(jiān)護,因此通常用于局域網(wǎng)高可靠性的分散系統(tǒng)中client/server應(yīng)用程序。例如視頻會議系統(tǒng),并不要求音頻視頻數(shù)據(jù)絕對的正確,只要保證連貫性就可以了,這種情況下顯然使用UDP會更合理一些。

TCP與UDP

面向報文的傳輸方式是應(yīng)用層交給UDP多長的報文,UDP就照樣發(fā)送,即一次發(fā)送一個報文。因此,應(yīng)用程序必須選擇合適大小的報文。若報文太長,則IP層需要分片,降低效率。若太短,會是IP太小。UDP對應(yīng)用層交下來的報文,既不合并,也不拆分,而是保留這些報文的邊界。這也就是說,應(yīng)用層交給UDP多長的報文,UDP就照樣發(fā)送,即一次發(fā)送一個報文。面向字節(jié)流的話,雖然應(yīng)用程序和TCP的交互是一次一個數(shù)據(jù)塊(大小不等),但TCP把應(yīng)用程序看成是一連串的無結(jié)構(gòu)的字節(jié)流。TCP有一個緩沖,當應(yīng)用程序傳送的數(shù)據(jù)塊太長,TCP就可以把它劃分短一些再傳送。如果應(yīng)用程序一次只發(fā)送一個字節(jié),TCP也可以等待積累有足夠多的字節(jié)后再構(gòu)成報文段發(fā)送出去。

TCP協(xié)議

  • Transmission Control Protocol,傳輸控制協(xié)議
  • 面向連接的協(xié)議
  • 需要三次握手建立連接
  • 需要四次揮手斷開連接
  • TCP報頭最小長度:20字節(jié)

三次握手的過程:

  1. 客戶端發(fā)送:SYN = 1, SEQ = X, 端口號
  2. 服務(wù)器回復(fù):SYN = 1, ACK = X + 1, SEQ = Y
  3. 客戶端發(fā)送:ACK = Y + 1, SEQ = X + 1

確認應(yīng)答信號ACK = 收到的SEQ + 1。連接建立中,同步信號SYN始終為1。連接建立后,同步信號SYN=0。

四次揮手過程

  1. A向B提出停止連接請求,F(xiàn)IN = 1
  2. B收到,ACK = 1
  3. B向A提出停止連接請求,F(xiàn)IN = 1
  4. A收到,ACK = 1

優(yōu)點:

  • 可靠,穩(wěn)定 1、傳遞數(shù)據(jù)前,會有三次握手建立連接 2、傳遞數(shù)據(jù)時,有確認、窗口、重傳、擁塞控制 3、傳遞數(shù)據(jù)后,會斷開連接節(jié)省系統(tǒng)資源

缺點:

  • 傳輸慢,效率低,占用系統(tǒng)資源高1、傳遞數(shù)據(jù)前,建立連接需要耗時2、傳遞數(shù)據(jù)時,確認、重傳、擁塞等會消耗大量時間以及CPU和內(nèi)存等硬件資源
  • 易被攻擊1、因為有確認機制,三次握手等機制,容易被人利用,實現(xiàn)DOS 、DDOS攻擊

如何保證接收的順序性:
TCP協(xié)議使用SEQ和ACK機制保證了順序性TCP的每個報文都是有序號的。確認應(yīng)答信號ACK=收到的SEQ+1

TCP的三次握手,和四次揮手,為什么需要三次握手,為什么要四次揮手

UDP協(xié)議

  • User Data Protocol,用戶數(shù)據(jù)包協(xié)議
  • 面向無連接的協(xié)議
  • UDP報頭只有8字節(jié)

簡介:

  • 傳輸數(shù)據(jù)之前源端和終端不建立連接,當它想傳送時就簡單地去抓取來自應(yīng)用程序的數(shù)據(jù),并盡可能快的把它扔到網(wǎng)絡(luò)上
  • 在發(fā)送端,UDP傳送數(shù)據(jù)的速度僅僅是受應(yīng)用程序生成數(shù)據(jù)的速度、計算機的能力和傳輸帶寬的限制
  • 在接收端,UDP把每個消息段放在隊列中,應(yīng)用程序每次從隊列中讀一個消息段
  • 由于傳輸數(shù)據(jù)不建立連接,因此也就不需要維護連接狀態(tài),包括收發(fā)狀態(tài)等,因此一臺服務(wù)機可同時向多個客戶機傳輸相同的消息
  • UDP信息包的標題很短,只有8個字節(jié),相對于TCP的20個字節(jié)信息包的額外開銷很小
  • 吞吐量不受擁擠控制算法的調(diào)節(jié),只受應(yīng)用軟件生成數(shù)據(jù)的速率、傳輸帶寬、源端和終端主機性能的限制
  • UDP使用盡最大努力交付,即不保證可靠交付,因此主機不需要維持復(fù)雜的鏈接狀態(tài)表。
  • UDP是面向報文的。發(fā)送方的UDP對應(yīng)用程序交下來的報文,在添加首部后就向下交付給IP層。既不拆分,也不合并,而是保留這些報文的邊界,因此,應(yīng)用程序需要選擇合適的報文大小。

使用“ping”命令來測試兩臺主機之間TCP/IP通信是否正常,其實“ping”命令的原理就是向?qū)Ψ街鳈C發(fā)送UDP數(shù)據(jù)包,然后對方主機確認收到數(shù)據(jù)包,如果數(shù)據(jù)包是否到達的消息及時反饋回來,那么網(wǎng)絡(luò)就是通的。

優(yōu)點:

  • 傳輸速率快1、傳輸數(shù)據(jù)前,不需要像TCP一樣建立連接2、傳輸數(shù)據(jù)時,沒有確認、窗口、重傳、擁塞控制等機制
  • 較安全1、由于沒有了TCP的一些機制,被攻擊者利用的漏洞就少了

缺點:

  • 不可靠,不穩(wěn)定1、由于沒有了TCP的機制,在數(shù)據(jù)傳輸時如果網(wǎng)絡(luò)不好,很可能丟包

用UDP協(xié)議通訊時怎樣得知目標機是否獲得了數(shù)據(jù)包

仿造TCP的做法,每發(fā)一個UDP包,都在里面加一個SEQ序號,接收方收到包后,將SEQ序號回復(fù)給發(fā)送方。如果發(fā)送方在指定時間以內(nèi)沒有收到回應(yīng),說明丟包了。

為什么UDP比TCP快

  1. TCP需要三次握手
  2. TCP有擁塞控制,控制流量等機制

為什么TCP比UDP可靠

  1. TCP是面向有連接的,建立連接之后才發(fā)送數(shù)據(jù);而UDP則不管對方存不存在都會發(fā)送數(shù)據(jù)。
  2. TCP有確認機制,接收端每收到一個正確包都會回應(yīng)給發(fā)送端。超時或者數(shù)據(jù)包不完整的話發(fā)送端會重傳。UDP沒有,因此可能丟包。

什么時候使用TCP

當對網(wǎng)絡(luò)通訊質(zhì)量有要求的時候,比如:整個數(shù)據(jù)要準確無誤的傳遞給對方,這往往用于一些要求可靠的應(yīng)用,比如HTTP、HTTPS、FTP等傳輸文件的協(xié)議,POP、SMTP等郵件傳輸?shù)膮f(xié)議。在日常生活中,常見使用TCP協(xié)議的應(yīng)用如下:瀏覽器,用的HTTPFlashFXP,用的FTPOutlook,用的POP、SMTPPutty,用的Telnet、SSHQQ文件傳輸

什么時候應(yīng)該使用UDP

當對網(wǎng)絡(luò)通訊質(zhì)量要求不高的時候,要求網(wǎng)絡(luò)通訊速度能盡量的快,這時就可以使用UDP。比如,日常生活中,常見使用UDP協(xié)議的應(yīng)用如下:QQ語音QQ視頻TFTP

TCP無邊界,UDP有邊界

TCP無邊界

客戶端分多次發(fā)送數(shù)據(jù)給服務(wù)器,若服務(wù)器的緩沖區(qū)夠大,那么服務(wù)器端會在客戶端發(fā)送完之后一次性接收過來,所以是無邊界的;

UDP有邊界

客戶端每發(fā)送一次,服務(wù)器端就會接收一次,也就是說發(fā)送多少次就會接收多少次,因此是有邊界的。

Socket編程的步驟

Server端Listen(監(jiān)聽)某個端口是否有連接請求,Client端向Server 端發(fā)出Connect(連接)請求,Server端向Client端發(fā)回Accept(接受)消息。一個連接就建立起來了。Server端和Client 端都可以通過Send,Write等方法與對方通信。

對于一個功能齊全的Socket,都要包含以下基本結(jié)構(gòu),其工作過程包含以下四個基本的步驟:

(1) 創(chuàng)建Socket;

(2) 打開連接到Socket的輸入/出流;

(3) 按照一定的協(xié)議對Socket進行讀/寫操作;

(4) 關(guān)閉Socket.(在實際應(yīng)用中,并未使用到顯示的close,雖然很多文章都推薦如此,不過在我的程序中,可能因為程序本身比較簡單,要求不高,所以并未造成什么影響。)

IO

IO框架主要用到什么設(shè)計模式

JDK的I/O包中就主要使用到了兩種設(shè)計模式:Adatper模式和Decorator模式。

NIO包有哪些結(jié)構(gòu)?分別起到的作用?

NIO

NIO針對什么情景會比IO有更好的優(yōu)化?

OKIO底層實現(xiàn)

json

JSON,fastjson 和 GSON的區(qū)別

1.json-lib

json-lib最開始的也是應(yīng)用最廣泛的json解析工具,json-lib 不好的地方確實是依賴于很多第三方包,包括commons-beanutils.jar,commons-collections-3.2.jar,commons-lang-2.6.jar,commons-logging-1.1.1.jar,ezmorph-1.0.6.jar,
對于復(fù)雜類型的轉(zhuǎn)換,json-lib對于json轉(zhuǎn)換成bean還有缺陷,比如一個類里面會出現(xiàn)另一個類的list或者map集合,json-lib從json到bean的轉(zhuǎn)換就會出現(xiàn)問題。

json-lib在功能和性能上面都不能滿足現(xiàn)在互聯(lián)網(wǎng)化的需求。

2.開源的Jackson

相比json-lib框架,Jackson所依賴的jar包較少,簡單易用并且性能也要相對高些。而且Jackson社區(qū)相對比較活躍,更新速度也比較快。

Jackson對于復(fù)雜類型的json轉(zhuǎn)換bean會出現(xiàn)問題,一些集合Map,List的轉(zhuǎn)換出現(xiàn)問題。Jackson對于復(fù)雜類型的bean轉(zhuǎn)換Json,轉(zhuǎn)換的json格式不是標準的Json格式

3.Google的Gson

Gson是目前功能最全的Json解析神器,Gson當初是為因應(yīng)Google公司內(nèi)部需求而由Google自行研發(fā)而來,但自從在2008年五月公開發(fā)布第一版后已被許多公司或用戶應(yīng)用。

Gson的應(yīng)用主要為toJson與fromJson兩個轉(zhuǎn)換函數(shù),無依賴,不需要例外額外的jar,能夠直接跑在JDK上。

而在使用這種對象轉(zhuǎn)換之前需先創(chuàng)建好對象的類型以及其成員才能成功的將JSON字符串成功轉(zhuǎn)換成相對應(yīng)的對象。類里面只要有g(shù)et和set方法,Gson完全可以將復(fù)雜類型的json到bean或bean到j(luò)son的轉(zhuǎn)換,是JSON解析的神器。

Gson在功能上面無可挑剔,但是性能上面比FastJson有所差距。

4.阿里巴巴的FastJson

Fastjson是一個Java語言編寫的高性能的JSON處理器,由阿里巴巴公司開發(fā)。無依賴,不需要例外額外的jar,能夠直接跑在JDK上。

FastJson在復(fù)雜類型的Bean轉(zhuǎn)換Json上會出現(xiàn)一些問題,可能會出現(xiàn)引用的類型,導致Json轉(zhuǎn)換出錯,需要制定引用。

FastJson采用獨創(chuàng)的算法,將parse的速度提升到極致,超過所有json庫。

xml

XML,解析XML的幾種方式的原理與特點:DOM、SAX、PULL

參考:http://www.cnblogs.com/HaroldTihan/p/4316397.html

JNI

參考:http://landerlyoung.github.io/blog/2014/10/16/java-zhong-jnide-shi-yong/

MD5加密原理,可否解密。

排序算法

常見排序算法的時間復(fù)雜度

Java 排序算法

參考:http://blog.csdn.net/qy1387/article/details/7752973

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內(nèi)容

  • 從三月份找實習到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,319評論 11 349
  • (一)Java部分 1、列舉出JAVA中6個比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨云閱讀 7,127評論 0 62
  • 一、多線程 說明下線程的狀態(tài) java中的線程一共有 5 種狀態(tài)。 NEW:這種情況指的是,通過 New 關(guān)鍵字創(chuàng)...
    Java旅行者閱讀 4,707評論 0 44
  • 夜已深,人未眠!淅瀝瀝的雨著實讓人心煩意亂, 雨將停,心卻亂! 并不寂寞,因為有寂寞陪伴著我! ...
    余越閱讀 412評論 0 1
  • 問:如何培養(yǎng)批判性思維? 答:1.多問How 2.多問why 3.多問why not 4.多與別...
    蘇不慢閱讀 3,185評論 4 51