Android面試相關知識

以前8月份開始複習一些基礎知識,並陸陸續續的總結了一些面試相關的東西,過久沒寫博客了,今天就作一個基礎知識的分享吧。css

無奈本人太蔡了,面試了這麼多家沒有收到一個offer。心灰意冷以後,遵從朋友建議,如今已經開始學後端相關的知識了,之後可能發的博客也不是音視頻相關的了,更多的是後端相關技術。html

首先複習基礎,這兒推薦github上的一個項目:java

LearningNoteandroid

而後就是一些進階須要掌握的知識 ,下面是我本身作的筆記,但願能幫到你們找到一個好的工做。c++

一. Android相關

1. mvc mvp mvvm三種架構模式

  • mvc:業務邏輯、數據、界面分離的一種模式,簡單的來講,就是經過controller來操做model層的數據,而且返回給view顯示。
    • activity不是標準的controller,隨着界面邏輯交互的複雜度提高,activity類的職責不斷增長,變得臃腫。
    • view和model相互耦合,不利於開發。
  • mvp:主要是提出了presenter層,做爲view和model之間溝通的橋樑。
    • 程序邏輯放在presenter中處理,徹底將view和model進行了分離,不容許他們之間溝通。
  • mvvm:主要是將presenter改成了viewmodel,和mvp相似,不一樣的是viewmodel跟view和model進行雙向綁定。
    • 使用了data binding

2. Android系統結構層次

Android系統架構分爲5層,從下到上依次爲 Linux內核層,硬件抽象層,系統運行庫層(Native),應用框架層,應用層。git

  • Linux內核層:Android的核心基於Linux內核,在此基礎上添加了Android的專用驅動(好比Binder)、系統的安全性、內存管理、進程管理等等。github

  • 硬件抽象層(HAL):有了核心還不行,你得須要運行到相應的硬件上才能實現本身的價值吧。而硬件抽象層就是硬件和Linux內核之間的接口,目的就是將硬件抽象化,保護硬件廠商的知識產權(Linux是有開源協議的)面試

  • 系統運行庫層:怎麼操縱硬件,顯示圖像到屏幕?這一層就是幹這個的,它分爲兩部分,分別是C++程序庫和Android運行時。算法

    1. C++程序庫:被Android系統中的不一樣組件使用,能夠經過應用框架層被開發者使用,下面是主要的程序庫:數據庫

      openGL ES 3D繪圖函數庫
      Media Framework 多媒體庫
      SQLite 關係型數據庫引擎
      SSL 安全套接層
    2. Android Runtime:ART虛擬機(5.0以後,Dalvik虛擬機被ART取代),ART在應用第一次安裝的時候,就會將字節碼預編譯成機器碼存儲到本地,這樣應用每次運行就無須執行編譯了(Dalvik是每次打開都要即時編譯),典型的以空間換時間

    3. 應用框架層:Framework層,這層代碼是用java編寫的,爲開發人員提供了API。

    4. 應用層。

3. Activity活動的啓動模式及應用場景

  1. standard: 默認的模式,新建一個Activity就在棧中新建一個activity實例。
  2. singleTop:棧頂複用模式,與standard相比棧頂複用能夠有效減小activity重複建立對資源的消耗 。 登陸頁面 ,wxpay等支付頁面
  3. singleTask:棧內單例模式,棧內只有一個activity實例,棧內已存activity實例,在其餘activity中start這個activity,Android直接把這個實例上面其餘activity實例踢出棧GC掉。主頁面 ,WebView頁面、掃一掃頁面 ,付款界面
  4. singleInstance:開闢一個新的棧存放activity。系統Launcher、鎖屏鍵、來電顯示等系統應用 。

4. Android進程間通訊的方式

1. Broadcast廣播,當某個程序向系統發送廣播時,其餘的應用程序只能被動地接收廣播數據
  2. Content Provider,多個應用程序之間數據共享的方式(跨進程共享數據) ,應用程序能夠完成對數據的增刪改查。Android系統自己也提供了不少的Content Provider,好比音頻,視頻,聯繫人等信息。
  3. 經過AIDL文件,其中AIDL也是經過binder實現進程間通訊的。
  4. socket
複製代碼
1.)傳統的IPC通訊方式

Android系統是基於Linux內核的,Linux提供了管道、消息隊列、共享內存和socket等IPC機制。那爲何Android還要提供Binder來實現IPC呢?主要是基於性能、穩定性和安全性方面的考慮。

性能:socket做爲通用接口,傳輸效率低,開銷大,主要用到跨網絡進程通訊。消息隊列、共享內存和管道採用存儲-轉發模式,數據拷貝至少須要兩次,共享內存雖然無需拷貝,可是控制複雜,難以使用。而binder只須要拷貝一次,性能上只次於共享內存。

穩定性:Binder 基於 C/S 架構,客戶端(Client)有什麼需求就丟給服務端(Server)去完成,架構清晰、職責明確又相互獨立,天然穩定性更好。

安全性:Android 爲每一個安裝好的 APP 分配了本身的 UID,故而進程的 UID 是鑑別進程身份的重要標誌。傳統的 IPC 只能由用戶在數據包中填入 UID/PID,但這樣不可靠,容易被惡意程序利用。可靠的身份標識只有由 IPC 機制在內核中添加。其次傳統的 IPC 訪問接入點是開放的,只要知道這些接入點的程序均可以和對端創建鏈接,無論怎樣都沒法阻止惡意程序經過猜想接收方地址得到鏈接。

2.)傳統IPC通訊原理

一般的作法是消息發送方將要發送的數據存放在內存緩存區中,經過系統調用進入內核態。而後內核程序在內核空間分配內存,開闢一塊內核緩存區,調用 copy_from_user() 函數將數據從用戶空間的內存緩存區拷貝到內核空間的內核緩存區中。一樣的,接收方進程在接收數據時在本身的用戶空間開闢一塊內存緩存區,而後內核程序調用 copy_to_user() 函數將數據從內核緩存區拷貝到接收進程的內存緩存區。這樣數據發送方進程和數據接收方進程就完成了一次數據傳輸,咱們稱完成了一次進程間通訊。

一次數據傳遞須要經歷:內存緩存區 --> 內核緩存區 --> 內存緩存區,須要 2 次數據拷貝

接收數據的緩存區由數據接收進程提供,可是接收進程並不知道須要多大的空間來存放將要傳遞過來的數據,所以只能開闢儘量大的內存空間或者先調用 API 接收消息頭來獲取消息體的大小,這兩種作法不是浪費空間就是浪費時間。

3.)Binder IPC實現

Binder IPC 機制中涉及到的內存映射經過 mmap() 來實現,mmap() 是操做系統中一種內存映射的方法。內存映射簡單的講就是將用戶空間的一塊內存區域映射到內核空間。映射關係創建後,用戶對這塊內存區域的修改能夠直接反應到內核空間;反以內核空間對這段區域的修改也能直接反應到用戶空間。

參考資料:寫給 Android 應用工程師的 Binder 原理剖析

5. ContentProvider的設計模式

參考:寫給Android App開發人員的Android底層知識

6. 多線程的實現方法(synchronized和lock的異同)

  1. 繼承Thread類建立線程
  2. 實現Runnable接口建立線程,推薦使用這種方式,能夠複用runnable
  3. 實現Callbale接口,經過FutureTask包裝器來建立一個帶返回值的線程

synchronized

在用法上,它是java的關鍵字,通常咱們不太須要關注他的鎖的釋放,代碼執行完畢或者報錯會自動釋放鎖,而且沒法判斷鎖的狀態。

lock

是一個接口,咱們使用ReentrantLock 比較多,有多個獲取鎖的方式,能夠trylock直接返回獲取成功或者失敗,線程不用一直等待。在finally中必需要釋放該鎖。

7. 說一下View的事件分發機制

爲何要有事件分發

注:引用G神的博客:點擊連接直達

Android中的view是樹形結構的,view可能會重疊在一塊兒,當咱們點擊的地方有多個view的時候,這個時間該給誰,這就是爲何要有事件分發。

先來看看view的樹形結構:

上面多出來兩個東西是phonewindowdecorview,其中,主題顏色和標題欄內容等主要就是decorview來負責顯示的,那PhoneWindow是作什麼的呢?

PhoneWindow 繼承window,而且是window惟一的實現類,window是一個抽象類,是全部視圖的最頂層容器,視圖的外觀和行爲都歸他管,不管是背景顯示,標題欄仍是事件處理都是他管理的範疇,它其實就像是View界的太上皇。

`DecorView` 是 `PhoneWindow` 的一個內部類,其職位至關於小太監,就是跟在 `PhoneWindow` 身邊專業爲 `PhoneWindow` 服務的,除了本身要幹活以外,也負責消息的傳遞,`PhoneWindow` 的指示經過 `DecorView` 傳遞給下面的 View,而下面 View 的信息也經過 `DecorView` 回傳給 `PhoneWindow`
複製代碼

事件分發、攔截、消費

類型 相關方法 Activity ViewGroup View
事件分發 dispatchTouchEvent
事件攔截 onInterceptTouchEvent X X
事件消費 onTouchEvent

Activity做爲原始的事件分發者,不須要攔截事件,若是須要這個事件不分發下去就好了。

一樣的,view在事件傳遞的最末端,也不須要攔截事件,不處理回傳回去就好了。

事件在收集以後最早傳遞給Activity,而後依次向下傳遞:

Activity -> PhoneWindow -> DectorView -> ViewGroup -> ... -> view
複製代碼

若是沒有任何View消費掉事件,那麼這個事件會按照反方向回傳,最終傳回給Activity,若是最後 Activity 也沒有處理,本次事件纔會被拋棄 :

Activity <- PhoneWindow <- DecorView <- ViewGroup <- ... <- View
複製代碼

上面的模式是一個很是經典的責任鏈模式

8. 說一下View從app啓動到顯示在界面上的繪製流程

注:參考連接

在activity的attach方法裏面,會建立一個PhoneWindow。

在onCreate中調用setContentView,setContentViewwindow的一個抽象方法,真正實現類是PhoneWindow

@Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {

//1.初始化
        //建立DecorView對象和mContentParent對象 ,並將mContentParent關聯到DecorView上
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();//Activity轉場動畫相關
        }

//2.填充Layout
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);//Activity轉場動畫相關
        } else {
        //將Activity設置的佈局文件,加載到mContentParent中
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }

        //讓DecorView的內容區域延伸到systemUi下方,防止在擴展時被覆蓋,達到全屏、沉浸等不一樣體驗效果。
        mContentParent.requestApplyInsets();

//3. 通知Activity佈局改變
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {

        //觸發Activity的onContentChanged方法 
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
複製代碼

核心方法就兩個:installDecor() 和 mLayoutInflater.inflate(layoutResID, mContentParent) ;

installDecor會建立一個DecorView 對象,該對象將做爲整個應用窗口的根視圖。而後配置不一樣窗口修飾屬性(style theme等)。

mLayoutInflater.inflate就是解析xml,深度優先地遞歸解析xml,一層層添加到root view上,最終返回root view.解析的部分大體包含兩點:1.解析出View對象,2.解析View對應的Params,並設置給View。

9. 知道什麼會引發ANR嗎 怎麼避免

有四種狀況會形成ANR發生:

  1. 5秒內沒法響應屏幕觸摸事件或鍵盤輸出
  2. 在執行前臺廣播的onReceive()函數時10秒沒有處理完成,後臺爲20秒
  3. 前臺服務20秒內,後臺服務在200秒內沒有執行完成
  4. ContentProviderpublish在10s內沒進行完

如何避免:

儘可能避免在主線程中作耗時操做。 多線程==>引出如何實現多線程,線程池的使用

如何分析ANR:

  1. 產生anr以後,會在data/anr/目錄下生成一個文件traces.txt
  2. Logcat中查看
  3. Java線程調用分析
  4. DDMS分析

10. 有作過app的性能優化嗎

  1. app啓動加速:一個app的啓動分爲三種不一樣的狀態,其中,咱們只須要對第一種狀態作優化。對於App來講, 咱們能夠控制的啓動時間線無外乎Application的onCreate,首屏Activity的渲染。

    1. 冷啓動:App沒有啓動過或者App進程被kill,系統中不存在該App進程。此時App的啓動須要建立App進程,加載相關資源,啓動main thread,初始化首屏Activity等。
    2. 熱啓動:App進程只是處於後臺, 系統只是將其從後臺帶到前臺, 展現給用戶。屏幕會顯示一個空白的窗口(顏色基於主題), 直至activity渲染完畢。
    3. 溫啓動:介於冷啓動和熱啓動之間, 通常來講在如下兩種狀況下發生,
      1. 用戶back退出了App, 而後又啓動. App進程可能還在運行, 可是activity須要重建
      2. 用戶退出App後, 系統可能因爲內存緣由將App殺死, 進程和activity都須要重啓, 可是能夠在onCreate中將被動殺死鎖保存的狀態(saved instance state)恢復
  2. 佈局優化 減小沒必要要的嵌套

    1. 儘可能不要嵌套使用RelativeLayout
    2. 儘可能不要在嵌套的LinearLayout中都使用weight屬性
    3. ConstraintLayout
    4. 善用TextView的Drawable減小布局層級
  3. 響應優化

  4. 內存優化 bitmap的使用,options的jusdecodeBounds屬性,設置只解析bitmap的寬高等,而後使用insimplesize對bitmap進行壓縮。在android2.3的時代,bitmap的回收須要調用recycler方法,而且置空,可是以後只須要進行置空操做。

    加載一張大圖:使用BitmapRegionDecode進行局部解碼,

    • decodeRegion(Rect rect, BitmapFactory.Options options) 指定rect區域獲取圖像,options參數不支持inPurgeable,其餘都支持

    lru算法的實現=> LinkedHashMap

  5. 電池使用優化

  6. 網絡優化

    參考:Android App優化, 要怎麼作?

11. 瞭解過Android最新技術嗎 使用過嗎

flutter

一步手機的刷新率爲60hz,當一幀圖像繪製完畢後準備繪製下一幀時,顯示器會發出一個垂直同步信號(如VSync), 60Hz的屏幕就會一秒內發出 60次這樣的信號。而這個信號主要是用於同步CPU、GPU和顯示器的。

通常地來講,計算機系統中

,CPU、GPU和顯示器以一種特定的方式協做:CPU將計算好的顯示內容提交給 GPU,GPU渲染後放入幀緩衝區,而後視頻控制器按照同步信號從幀緩衝區取幀數據傳遞給顯示器顯示。

kotlin

面試幾家以後發現都沒有問,因此略過

二. JAVA相關

1. 說一下JAVA的GC以及內存模型

GC:垃圾回收器,自動釋放垃圾佔用的空間,讓建立的對象不須要像c/c++那樣手動delete、free掉。

GC是在何時,對什麼東西,作了什麼事情?

何時

Java堆內存不足時,GC會被調用。當應用線程在運行,並在運行過程當中建立新對象,若這時內存空間不足,JVM就會強制地調用GC線程,以便回收內存用於新的分配。若GC一次以後仍不能知足內存分配的要求,JVM會再進行兩次GC做進一步的嘗試,若仍沒法知足要求,則 JVM將報「out of memory」的錯誤,Java應用將中止。

minor gc/full gc的觸發條件、OOM的觸發條件,下降GC的調優的策略 (深刻理解jvm)

minor gc :當新生代的eden區滿了的時候,會觸發gc

full gc: 當老年代空間不足,方法區空間不足,minor gc進入老年代的時候。大對象

對什麼東西

從gc root搜索不到,並且通過第一次標記、清理後,仍然沒有復活的對象

作了什麼事情

JVM將堆分紅了二個大區新生代(Young)和老年代(Old),新生代又被進一步劃分爲Eden和Survivor區,而Survivor由FromSpace和ToSpace組成。

Young中的98%的對象都是死朝生夕死,因此將內存分爲一塊較大的Eden和兩塊較小的Survivor一、Survivor2,JVM默認分配是8:1:1,每次調用Eden和其中的Survivor1(FromSpace),當發生回收的時候,將Eden和Survivor1(FromSpace)存活的對象複製到Survivor2(ToSpace)

注:分紅三塊是爲了充分利用內存。原來是隻有一塊內存,在這上面作標記清除算法,可是gc以後會存在大量不連續的空間,因此有人提出將內存一分爲二,將第一塊內存存活的對象轉移到第二塊內存,而後將第一塊內存gc。

新生代的GC(Minor GC):新生代一般存活時間較短基於Copying算法進行回收,所謂Copying算法就是掃描出存活的對象,並複製到一塊新的徹底未使用的空間中,對應於新生代,就是在Eden和FromSpace或ToSpace之間copy。新生代採用空閒指針的方式來控制GC觸發,指針保持最後一個分配的對象在新生代區間的位置,當有新的對象要分配內存時,用於檢查空間是否足夠,不夠就觸發GC。當連續分配對象時,對象會逐漸從Eden到Survivor,最後到老年代。

老年代的GC(Major GC/Full GC):老年代與新生代不一樣,老年代對象存活的時間比較長、比較穩定,所以採用標記(Mark)算法來進行回收,所謂標記就是掃描出存活的對象,而後再進行回收未被標記的對象,回收後對用空出的空間要麼進行合併、要麼標記出來便於下次進行分配,總之目的就是要減小內存碎片帶來的效率損耗。

jvm內存模型(注意和java內存模型區分,java內存模型和併發編程相關)

  1. 程序計數器:存放每一個程序下一步將執行的jvm指令,若是是native方法,則不會存儲任何信息
  2. jvm棧:是線程私有的,每一個線程建立的同時都會建立jvm棧
  3. 堆:JVM用來存儲對象實例以及數組值的區域,能夠認爲Java中全部經過new建立的對象的內存都在此分配,Heap中的對象的內存須要等待GC進行回收。
    1. 堆是JVM中全部線程共享的,所以在其上進行對象內存的分配均須要進行加鎖,這也致使了new對象的開銷是比較大的
    2. Sun Hotspot JVM爲了提高對象內存分配的效率,對於所建立的線程都會分配一塊獨立的空間TLAB(Thread Local Allocation Buffer),其大小由JVM根據運行的狀況計算而得,在TLAB上分配對象時不須要加鎖,所以JVM在給線程的對象分配內存時會盡可能的在TLAB上分配,在這種狀況下JVM中分配對象內存的性能和C基本是同樣高效的,但若是對象過大的話則仍然是直接使用堆空間分配
    3. TLAB僅做用於新生代的Eden Space,所以在編寫Java程序時,一般多個小的對象比大的對象分配起來更加高效。
  4. 方法區(持久代):存放了所加載的類的信息(名稱、修飾符等)、類中的靜態變量、類中定義爲final類型的常量、類中的Field信息、類中的方法信息,當開發人員在程序中經過Class對象中的getName、isInterface等方法來獲取信息時,這些數據都來源於方法區域,同時方法區域也是全局共享的,在必定的條件下它也會被GC,當方法區域須要使用的內存超過其容許的大小時,會拋出OutOfMemory的錯誤信息。jdk1.8中被移除了,改用metaspace
  5. 本地方法棧:JVM採用本地方法棧來支持native方法的執行,此區域用於存儲每一個native方法調用的狀態
  6. 運行常量池:存放的爲類中的固定的常量信息、方法和Field的引用信息等,其空間從方法區域中分配。JVM在加載類時會爲每一個class分配一個獨立的常量池,可是運行時常量池中的字符串常量池是全局共享的。

2. Java內存模型

參考:全面理解java內存模型,深刻理解java虛擬機

Java內存模型定義了多線程之間共享變量的可見性以及如何在須要的時候對共享變量進行同步。

3. JAVA的類加載器

類從被加載到虛擬機內存中開始,到卸載出內存中爲止,它的整個生命週期以下:

  1. 加載:主要是在內存中生成表明這個類的java.lang.class對象
  2. 驗證:確保class文件中的信息符合當前虛擬機的要求,而且不會危害虛擬機自己
  3. 準備:爲類變量分配內存並設置類變量初始值的階段
  4. 解析(不肯定順序)將常量池中的符號引用替換爲直接引用的過程
  5. 初始化:在準備階段是設置初始值,而在這個階段是根據咱們制定的設置值
  6. 使用(不肯定順序)
  7. 卸載

類加載模型

雙親委派模型,從java虛擬機的角度講,只存在兩種不一樣的類加載器,啓動類加載器和全部的其餘類加載器,啓動類加載器使用C++語言實現,是虛擬機的一部分,其餘的類加載器由java語言實現,獨立於虛擬機外,對於java開發人員來講,類加載器劃分得更細:

  1. 啓動類加載器(Bootstrap ClassLoader):加載虛擬機識別的類庫
  2. 擴展類加載器:
  3. 應用程序類加載器

雙親委派的工做過程:若是一個類加載器收到了類加載的請求,首先它不會本身去嘗試加載這個類,而是把這個請求委派給父類去完成,最終都會傳送到頂層的啓動類加載器中,只有當父加載器反饋本身沒法完成這個加載請求的時候,子加載器纔會嘗試本身去加載。

Android中經常使用的類加載器有:DexClassLoaderPathClassLoader,這兩個類都繼承自BaseDexClassLoader

PathClassLoader用於加載內部的Dex文件,DexClassLoader多傳了一個optimizedDirectory參數 ,能夠用來加載外部的apk文件(插件化技術的核心)

4. 熱更新原理

代碼修復主要有兩大方案,一種是阿里系的底層替換方案,另外一種是騰訊系的類加載方案,這兩種方案各有優劣。

底層替換方案限制比較多,可是時效性最好,加載快,當即見效

類加載方案時效性差,須要冷啓動才能見效,但修復範圍廣,限制少。

底層替換方案

直接在已加載類中替換掉原有方法,即在原來類基礎上進行修改,所以沒法實現增減原有類方法或字段,這樣會破壞原有類的結構。

熱修復原理以及類加載流程

類加載方案

在app從新啓動後讓Classloader加載新的類。由於當app運行到一半時,所需發生變動的類已經被加載過,而在Android上沒法對一個類進行卸載操做,若不重啓,原來的類還存儲於虛擬機中,新類沒法被加載。所以只有在下次重啓時,在業務邏輯運行以前搶先加載補丁中的新類,這樣後續訪問此類時,纔會Resolve爲新類,從而達到熱修復的目的。

5. 線程池有哪些?使用場景

參考:深刻源碼分析Java線程池的實現原理

池化技術

簡單的來說就是提早保存大量的資源,以備不時之需。在機器資源有限的狀況下,使用池化技術能夠大大的提升資源的利用率。比較典型的池化技術有線程池、鏈接池、內存池、對象池等。

爲何使用線程池

  1. 在任務衆多的狀況下,系統要爲每個任務建立一個線程,而任務執行完畢後會銷燬每個線程,因此會形成線程頻繁地建立與銷燬
  2. 多個線程頻繁地建立會佔用大量的資源,而且在資源競爭的時候就容易出現問題,同時這麼多的線程缺少一個統一的管理,容易形成界面的卡頓。
  3. 多個線程頻繁地銷燬,會頻繁地調用GC機制,這會使性能下降,又很是耗時。

常見的線程池:

  1. ThreadPoolExecutor :基本線程池
  2. FixedThreadPool :可重用固定線程池,執行長期的任務,性能好不少
  3. CachedThreadPool :按需建立,執行不少短時間異步的小程序或者負載較輕的服務器
  4. SingleThreadPool:單個核線的fixed,一個任務一個任務執行的場景
  5. ScheduledThreadPool:定時延時執行,週期性執行任務的場景

上面的2-5的線程池,都是基於第一個基本線程池實現的,不一樣的地方在於核心線程數和存聽任務的隊列類型不一樣。

如何使用

Executors這個類中封裝了不少方法。

有兩種方法提交任務,submit和execute,其中submit就是將runnable包裝成FutureTask 而已,最終調用的仍是execuet,因此咱們看execute的實現過程

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) { //直接新建核心線程
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) { //線程池是否在運行,添加到隊列裏等待
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command)) //線程池若是沒有在運行,移除隊列並拒絕任務
                reject(command);
            else if (workerCountOf(recheck) == 0)  //核心線程滿時,建立非核心線程執行任務
                addWorker(null, false);
        }
        else if (!addWorker(command, false)) //拒絕
            reject(command);
    }
複製代碼

主要的工做是:

  1. 工做線程數小於核心線程數時,直接新建核心線程執行任務
  2. 大於核心線程數時,將任務添加進等待隊列
  3. 核心線程滿時,建立非核心線程執行任務
  4. 工做線程數大於最大線程數時,拒絕任務

三. 網絡和算法相關

1. 說一下你對TCP/IP的理解

阮一峯-互聯網協議入門

2. https加密過程

注:參考 圖解HTTPS協議加密解密全過程

在傳輸層和應用層之間添加了一個SSL層,變成HTTP先和SSL通訊,再由SSL和TCP通訊

加密方式有兩種:

對稱密鑰加密:加密和解密同用一個密鑰。加密時就必須將密鑰傳送給對方。那密鑰如何保證安全傳輸的?這是一個先有雞仍是先有蛋的問題,傳輸內容怎麼加密的密鑰也能夠這麼加密

非對稱加密:使用一對非對稱的密鑰。一把叫作私有密鑰,一把叫作公開密鑰。私有密鑰不能讓其餘任何人知道,而公開密鑰則能夠隨意發佈,任何人均可以得到。能夠想象成一把鑰匙和一個鎖頭,只是全世界只有你一我的有這把鑰匙,你能夠把鎖頭給別人,別人能夠用這個鎖把重要的東西鎖起來,而後發給你,由於只有你一我的有這把鑰匙,因此只有你才能看到被這把鎖鎖起來的東西。

過程:

  1. 客戶端發起Https請求,帶上支持的加密協議以及版本
  2. 傳輸證書:服務端配須要採用HTTPS協議的服務器必需要有一套數字證書,能夠本身製做,也能夠向組織申請。區別就是本身頒發的證書須要客戶端驗證經過,才能夠繼續訪問,而使用受信任的公司申請的證書則不會彈出提示頁面。服務端給客戶端傳輸公鑰,這個公鑰包含了不少信息,如證書的頒發機構,過時時間等等 。
  3. 客戶端解析證書:這部分工做是由客戶端的TLS來完成的,首先會驗證公鑰是否有效,好比頒發機構,過時時間等等。若是證書沒有問題,那麼就生成一個隨機值。而後用證書對該隨機值進行加密。
  4. 傳送加密信息:用證書加密後的隨機值,目的就是讓服務端獲得這個隨機值,之後客戶端和服務端的通訊就能夠經過這個隨機值來進行加密解密了。
  5. 服務端解密隨機值,並用這個值加密內容:服務端用私鑰解密後,獲得了客戶端傳過來的隨機值(私鑰),而後把內容經過該值進行對稱加密。所謂對稱加密就是,將信息和私鑰經過某種算法混合在一塊兒,這樣除非知道私鑰,否則沒法獲取內容,而正好客戶端和服務端都知道這個私鑰,因此只要加密算法夠彪悍,私鑰夠複雜,數據就夠安全。
  6. 傳輸加密後的信息
  7. 客戶端解密

3. 操做系統中外部設備與CPU之間怎麼通訊

注:一步步編寫操做系統

任何不兼容的問題,均可以經過增長一「層」來解決。在cpu和外設之間的這一層就是IO接口。IO接口形式不限,它能夠是個電路板,也能夠是塊芯片,甚至能夠是個插槽,它的做用就是在cpu和外設之間相互作協調轉換,如cpu和外設速度不匹配,它就是變速箱,cpu和外設信號不通用,它就是翻譯機。

4. 散列函數如何解決衝突

開放定址法

一旦發生衝突,就去尋找下一個空的散列地址,只要散列表足夠大,空的散列地址總能找到

再散列函數法

實現準備多個計算散列值得函數,發生衝突以後,換一種計算散列值的函數。

鏈地址法

在發生衝突的地方插入一條單鏈表,將衝突的元素都放到這個鏈表裏。

java的hashmap就是使用的這種方法解決衝突 >>經過數組+鏈表+(紅黑樹,jdk1.8以後對hashmap作了優化)的方式,將hashcode相同的元素串成一串。

公共溢出區法

發生衝突以後的都存儲到公共溢出區

5. 基本排序算法以及實現原理

推薦書籍:大話數據結構

多去leetcode刷題

  1. 冒泡排序
  2. 插入排序
  3. 選擇排序
  4. 歸併排序
  5. 快速排序
  6. 希爾排序
  7. 堆排序

隊列的數據結構

隊列一般能夠分爲兩種類型:

  1. 順序隊列,採用順序存儲,當長度肯定時使用
  2. 鏈式隊列,採用鏈式存儲,長度不肯定時使用(由鏈表實現)
相關文章
相關標籤/搜索