1. 程式人生 > >聊聊基礎

聊聊基礎

數據庫 否則 不變 策略 tar 區別 hashmap類 原子變量 新的

摘要:最近和女友聊天,說我的工作需要作出調整,當前狀態下壓力太大,急需通過提供自身的專業技能來作出改變,所以便有了這個基礎知識的整理。本來這個帖子是發布在簡書的,因為考慮到簡書比較好編輯和閱覽,但是當我發布到簡書後,女友竟然驚訝和肯定我終於開始寫博客了,於是比較汗顏,就還是回歸發布到這裏來吧。後續針對這些基礎知識做更深次,更全面的研究,本基礎的一些問題來源:http://blog.csdn.net/exceptional_derek/article/details/69525715

一:基礎類

1.hashmap的基本原理,內部數據結構,put操作的整體流程,是否線程安全以及為什麽?jdk8對hashmap做了哪些優化?

答:HashMap是基於哈希表的Map接口的非同步實現。此實現提供所有可選的映射操作,並允許使用null值和null鍵。此類不保證映射的順序,特別是它不保證該順序恒久不變,HashMap實際上是一個“鏈表散列”的數據結構,即數組和鏈表的結合體。我們往HashMap中put元素的時候,先根據key的hashCode重新計算hash值,根據hash值得到這個元素在數組中的位置(即下標),如果數組該位置上已經存放有其他元素了,那麽在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放在鏈尾。如果數組該位置上沒有元素,就直接將該元素放到此數組中的該位置上。對HashMap的操作不是線程安全的,通過觀察源碼發現,當多個線程在某一個時刻同時對HashMap做結構性的修改,我們可以看到整個方法實現中沒有任何的同步機制,那麽存在一個線程獲取或者修改數據結構時,存在另外一個線程獲取了一個錯誤的結果。jdk8對hashMap的數據結構的改變有個調整,當數組達到一定的閾值時,bucket就會由鏈表轉換為紅黑樹的方式進行存儲,而不是進行table的擴容。

2.String類為什麽是不可變的?StringBuilder和StringBuffer的區別,字符串常量池,StringBuffer為什麽是線程安全?加號的底層原理?

答:首先String類是用final關鍵字修飾,這說明String不可繼承,String類的成員字段value是個char[]數組,而且是用final修飾的。final修飾的字段創建以後就不可改變。不可變的好處:1.1.參考java字符串池的設計模式。比如兩個字符串值相等的變量,他們只會生成一個對象放到常量池中,然後兩個變量都指向它,提升效率。1.2.安全性,如果String類可以被修改,那麽在多線程的情況下會造成安全漏洞。2.1 StringBuilder和StringBuffer的區別:他們都是創建字符串的常用類,長度都是可以擴充的,實現了CharSequence接口。StringBuilder非線程安全,StringBuffer線程安全,所以通常在單線程環境下可以考慮是用StringBuilder來提升速度和效率,而在多線程的環境下則應該使用SringBuffer來保證線程安全。

3.反射、accessible,動態代理的原理,jdk動態代理與cglib的區別與各自的實現原理?

答:反射的機制是在編譯時並不確定的哪個類被jvm加載,在程序運行的時候才加載、探知、自審。動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因為采用的是繼承,所以不能對final修飾的類進行代理。兩者區別:jdk的代理是利用反射生成字節碼,並生成對象,前提是只能代理實現了接口的類,cglib是直接修改目標類的字節碼生成對象,因為原理是繼承,所以不能對final修飾的類進行代理。http://rejoy.iteye.com/blog/1627405https://my.oschina.net/tearsky/blog/635321

4.自動裝箱,賦值操作,在內存裏面是如何實現的?

答:自動裝箱是將內置類型轉換為對應的包裝類型,在自動裝箱的過程中,程序會創建一個包裝類型的對象,然後將該變量指向這個新創建的對象,完成裝箱操作。

5.接口和抽象類的區別

答:

1、抽象類和接口都不能直接實例化,如果要實例化,抽象類變量必須指向實現所有抽象方法的子類對象,接口變量必須指向實現所有接口方法的類對象。

2、抽象類要被子類繼承,接口要被類實現。

3、接口只能做方法申明,抽象類中可以做方法申明,也可以做方法實現。

4、接口裏定義的變量只能是公共的靜態的常量,抽象類中的變量是普通變量。

5、抽象類裏的抽象方法必須全部被子類所實現,如果子類不能全部實現父類抽象方法,那麽該子類只能是抽象類。同樣,一個實現接口的時候,如不能全部實現接口方法,那麽該類也只能為抽象類。

6、抽象方法只能申明,不能實現,接口是設計的結果 ,抽象類是重構的結果。

7、抽象類裏可以沒有抽象方法。

8、如果一個類裏有抽象方法,那麽這個類只能是抽象類。

9、抽象方法要被實現,所以不能是靜態的,也不能是私有的。

10、接口可繼承接口,並可多繼承接口,但類只能單根繼承。

6.concurrenthashmap的原理,內部數據結構,如何提高並發性,jdk8中做了哪些優化。

答:ConcurrentHashMap允許多個修改操作並發進行,其關鍵在於使用了鎖分離技術。它使用了多個鎖來控制對hash表的不同部分進行的修改。ConcurrentHashMap內部使用段(Segment)來表示這些不同的部分,每個段其實就是一個小的hash table,它們有自己的鎖。只要多個修改操作發生在不同的段上,它們就可以並發進行。

改進一:取消segments字段,直接采用transient volatile HashEntry[] table保存數據,采用table數組元素作為鎖,從而實現了對每一行數據進行加鎖,進一步減少並發沖突的概率。

改進二:將原先table數組+單向鏈表的數據結構,變更為table數組+單向鏈表+紅黑樹的結構。對於hash表來說,最核心的能力在於將key hash之後能均勻的分布在數組中。如果hash之後散列的很均勻,那麽table數組中的每個隊列長度主要為0或者1。但實際情況並非總是如此理想,雖然ConcurrentHashMap類默認的加載因子為0.75,但是在數據量過大或者運氣不佳的情況下,還是會存在一些隊列長度過長的情況,如果還是采用單向列表方式,那麽查詢某個節點的時間復雜度為O(n);因此,對於個數超過8(默認值)的列表,jdk1.8中采用了紅黑樹的結構,那麽查詢的時間復雜度可以降低到O(logN),可以改進性能。

7.hashset的原理?

答:HashSet中add方法調用的是底層HashMap中的put()方法,而如果是在HashMap中調用put,首先會判斷key是否存在,如果key存在則修改value值,如果key不存在這插入這個key-value。而在set中,因為value值沒有用,也就不存在修改value值的說法,因此往HashSet中添加元素,首先判斷元素(也就是key)是否存在,如果不存在這插入,如果存在著不插入,這樣HashSet中就不存在重復值。

8.GC原理,分代機制,可達性分析?

答:對傳統的、基本的GC實現來說,由於它們在GC的整個工作過程中都要“stop-the-world”,如果能想辦法縮短GC一次工作的時間長度就是件重要的事情。如果說收集整個GC堆耗時太長,那不如只收集其中的一部分?於是就有好幾種不同的劃分(partition)GC堆的方式來實現部分收集,而分代式GC就是這其中的一個思路。

9.JVM參數有哪幾種,如何調優?

-Xmx2g //JVM最大允許分配的堆內存,按需分配

-Xms2g //JVM初始分配的堆內存,一般和Xmx配置成一樣以避免每次gc後JVM重新分配內存。-Xmn256m //年輕代內存大小,整個JVM內存=年輕代 + 年老代 + 持久代年輕代分三個區, 分別是enden區和兩個survivor區。

10.JMM特性有哪些?

1.可見性:JMM提供了volatile變量定義、final、synchronized塊來保證可見性。

2.有序性:這個概念是相對而言的,如果在本線程內,所有的操作都是有序的,如果在一個線程觀察另一個線程,所有的操作都是無序的,前句是“線程內表現為串行行為”,後句是“指令的重排序”和“工作內存和主內存同步延遲”現象,模型提供了volatile和synchronized來保證線程之間操作的有序性。

3.重排序:在執行程序時為了提高性能,編譯器和處理器常常會對指令做重排序(編譯器、處理器),就是因為這些重排序,所以可能會導致多線程程序出現內存可見性問題(數據安全問題)和有序性問題。可見性、原子性、有序性.

11、什麽是跳表?

二、多線程

1、線程有幾種狀態?之間是如何切換的?

答:新建狀態、就緒狀態、運行狀態、阻塞狀態及死亡狀態。主要是通過獲取鎖標記來獲取對該資源的使用權限,當對象調用了start()進入到就緒狀態,進入就緒後,當該對象被操作系統選中,獲得CPU時間片就會進入運行狀態;接下來的狀態切換就會比較復雜,主要通過線程調用不同的方法,就會切換不同的運行狀態。

2、volatile的作用(兩點),volatile的原理與應用場景。

答:volatile讓變量每次在使用的時候,都從主存中取。(1.將當前處理器緩存行的數據會寫回到系統內存,2.這個寫回內存的操作會引起在其他CPU裏緩存了該內存地址的數據無效。)而不是從各個線程的“工作內存”。volatile具有synchronized關鍵字的“可見性”,但是沒有synchronized關鍵字的“並發正確性”,也就是說不保證線程執行的有序性,volatile變量對於每次使用,線程都能得到當前volatile變量的最新值。但是volatile變量並不保證並發的正確性。

3、線程安全是什麽?如何做到線程安全?怎麽判斷一個類是不是線程安全?

答:類要成為線程安全的,首先必須在單線程環境中有正確的行為。如果一個類實現正確(這是說它符合規格說明的另一種方式),那麽沒有一種對這個類的對象的操作序列(讀或者寫公共字段以及調用公共方法)可以讓對象處於無效狀態,觀察到對象處於無效狀態、或者違反類的任何不可變量、前置條件或者後置條件的情況。此外,一個類要成為線程安全的,在被多個線程訪問時,不管運行時環境執行這些線程有什麽樣的時序安排或者交錯,它必須仍然有如上所述的正確行為,並且在調用的代碼中沒有任何額外的同步。其效果就是,在所有線程看來,對於線程安全對象的操作是以固定的、全局一致的順序發生的。正確性與線程安全性之間的關系非常類似於在描述 ACID(原子性、一致性、獨立性和持久性)事務時使用的一致性與獨立性之間的關系:從特定線程的角度看,由不同線程所執行的對象操作是先後(雖然順序不定)而不是並行執行的。

4、線程同步有幾種方式?為何要使用同步?

答: java允許多線程並發控制,當多個線程同時操作一個可共享的資源變量時(如數據的增刪改查), 將會導致數據不準確,相互之間產生沖突,因此加入同步鎖以避免在該線程沒有完成操作之前,被其他線程的調用, 從而保證了該變量的唯一性和準確性。

同步的實現方式總共分為七種:

1.同步方法 : 即有synchronized關鍵字修飾的方法,由於java的每個對象都有一個內置鎖,當用此關鍵字修飾方法時,內置鎖會保護整個方法。在調用該方法前,需要獲得內置鎖,否則就處於阻塞狀態。

2.同步代碼塊:即有synchronized關鍵字修飾的語句塊,被該關鍵字修飾的語句塊會自動被加上內置鎖,從而實現同步

3.使用特殊域變量(volatile)實現線程同步

a.volatile關鍵字為域變量的訪問提供了一種免鎖機制。

b.使用volatile修飾域相當於告訴虛擬機該域可能會被其他線程更新。

c.因此每次使用該域就要重新計算,而不是使用寄存器中的值。

d.volatile不會提供任何原子操作,它也不能用來修飾final類型的變量。

4.使用重入鎖實現線程同步.

java.util.concurrent包下的ReentrantLock類是可重入、互斥、實現了Lock接口的鎖,它與使用synchronized方法和快具有相同的基本行為和語義,並且擴展了其能力.

5.使用局部變量實現線程同步,如果使用ThreadLocal管理變量,則每一個使用該變量的線程都獲得該變量的副本, 副本之間相互獨立,這樣每一個線程都可以隨意修改自己的變量副本,而不會對其他線程產生影響

6.使用阻塞隊列實現線程同步,前面5種同步方式都是在底層實現的線程同步,但是我們在實際開發當中,應當盡量遠離底層結構。使用javaSE5.0版本中新增的java.util.concurrent包將有助於簡化開發。使用LinkedBlockingQueue來實現線程的同步, LinkedBlockingQueue是一個基於已連接節點的,範圍任意的blocking queue。隊列是先進先出的順序(FIFO)。

7.使用原子變量實現線程同步,需要使用線程同步的根本原因在於對普通變量的操作不是原子的。原子操作就是指將讀取變量值、修改變量值、保存變量值看成一個整體來操作,即-這幾種行為要麽同時完成,要麽都不完成。在java的util.concurrent.atomic包中提供了創建了原子類型變量的工具類,使用該類可以簡化線程同步。其中AtomicInteger 表可以用原子方式更新int的值,可用在應用程序中(如以原子方式增加的計數器),但不能用於替換Integer;可擴展Number,允許那些處理機遇數字類的工具和實用工具進行統一訪問。

5、threadlocal的原理

答:ThreadLocal提供了set和get訪問器用來訪問與當前線程相關聯的線程局部變量。當線程中的threadlocalmap是null的時候,會調用createmap創建一個map。同時根據函數參數設置上初始值。也就是說,當前線程的threadlocalmap是在第一次調用set的時候創建map並且設置上相應的值的。在ThreadLocal的set函數中,可以看到,其中的map.set(this, value);把當前的threadlocal傳入到map中作為鍵,也就是說,在不同的線程的threadlocals變量中,都會有一個以你所聲明的那個線程局部變量threadlocal作為鍵的key-value。假設說聲明了N個這樣的線程局部變量變量,那麽在線程的ThreadLocalMap中就會有n個分別以你的線程局部變量作為key的鍵值對。

6、synchronized是如何實現的?

答:每個對象有一個監視器鎖(monitor)。當monitor被占用時就會處於鎖定狀態,線程執行monitorenter指令時嘗試獲取monitor的所有權,過程如下:

1、如果monitor的進入數為0,則該線程進入monitor,然後將進入數設置為1,該線程即為monitor的所有者。

2、如果線程已經占有該monitor,只是重新進入,則進入monitor的進入數加1.

3.如果其他線程已經占用了monitor,則該線程進入阻塞狀態,直到monitor的進入數為0,再重新嘗試獲取monitor的所有權。

對於方法的同步,方法的同步並沒有通過指令monitorenter和monitorexit來完成(理論上其實也可以通過這兩條指令來實現),不過相對於普通方法,其常量池中多了ACC_SYNCHRONIZED標示符。JVM就是根據該標示符來實現方法的同步的:當方法調用時,調用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標誌是否被設置,如果設置了,執行線程將先獲取monitor,獲取成功之後才能執行方法體,方法執行完後再釋放monitor。在方法執行期間,其他任何線程都無法再獲得同一個monitor對象。 其實本質上沒有區別,只是方法的同步是一種隱式的方式來實現,無需通過字節碼來完成。

7、sleep和wait的區別?

答:sleep()方法導致了程序暫停執行指定的時間,讓出cpu給其他線程,但是他的監控狀態依然保持者,當指定的時間到了又會自動恢復運行狀態。在調用sleep()方法的過程中,線程不會釋放對象鎖。而當調用wait()方法的時候,線程會放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象調用notify()方法後本線程才進入對象鎖定池準備,獲取對象鎖進入運行狀態。

8、線程池有幾種?各自的應用場景。

答:1.newFixedThreadPool創建一個指定工作線程數量的線程池。每當提交一個任務就創建一個工作線程,如果工作線程數量達到線程池初始的最大數,則將提交的任務存入到池隊列中。

2.newCachedThreadPool創建一個可緩存的線程池。這種類型的線程池特點是:

1).工作線程的創建數量幾乎沒有限制(其實也有限制的,數目為Interger. MAX_VALUE), 這樣可靈活的往線程池中添加線程。

2).如果長時間沒有往線程池中提交任務,即如果工作線程空閑了指定的時間(默認為1分鐘),則該工作線程將自動終止。終止後,如果你又提交了新的任務,則線程池重新創建一個工作線程。

3.newSingleThreadExecutor創建一個單線程化的Executor,即只創建唯一的工作者線程來執行任務,如果這個線程異常結束,會有另一個取代它,保證順序執行(我覺得這點是它的特色)。單工作線程最大的特點是可保證順序地執行各個任務,並且在任意給定的時間不會有多個線程是活動的。

4.newScheduleThreadPool創建一個定長的線程池,而且支持定時的以及周期性的任務執行,類似於Timer。

總結:

一.FixedThreadPool是一個典型且優秀的線程池,它具有線程池提高程序效率和節省創建線程時所耗的開銷的優點。但是,在線程池空閑時,即線程池中沒有可運行任務時,它不會釋放工作線程,還會占用一定的系統資源。

二.CachedThreadPool的特點就是在線程池空閑時,即線程池中沒有可運行任務時,它會釋放工作線程,從而釋放工作線程所占用的資源。但是,但當出現新任務時,又要創建一新的工作線程,又要一定的系統開銷。並且,在使用CachedThreadPool時,一定要註意控制任務的數量,否則,由於大量線程同時運行,很有會造成系統癱瘓。

9、線程池的原理,主要有幾個參數?線程池滿了怎麽辦?

答:

減少在創建和銷毀線程上所花的時間以及系統資源的開銷。

如不使用線程池,有可能造成系統創建大量線程而導致消耗完系統內存以及”過度切換”。

10、Semaphore、countdownlatch、futureTask

答:

11、submit和execute的區別。

答:execute(Runnable x) 沒有返回值。可以執行任務,但無法判斷任務是否成功完成。——實現Runnable接口

submit(Runnable x) 返回一個future。可以用這個future來判斷任務是否成功完成。——實現Callable接口。

12、Future接口的幾個主要方法

答:

13、創建線程有幾種方式

答:1.繼承Thread類創建線程類

2.通過Runnable接口創建線程類

3.通過Callable和Future創建線程

14、可重入鎖是如何實現的

答:

三、數據庫

1、MySQL索引原理?為什麽是B+樹?有什麽優點?

1.文件很大,不可能全部存儲在內存中,故要存儲到磁盤上,索引的結構組織要盡量減少查找過程中磁盤I/O的存取次數,

2、事務隔離級別有哪幾種?mysql默認的隔離級別是?臟讀、幻讀、不可重復讀是什麽情況?

答:,由低到高分別為Read uncommitted 、Read committed 、Repeatable read 、Serializable,

3、MVCC原理

4、mysql有哪幾種鎖?

答:共享讀鎖,獨占寫鎖。根據數據引擎的不同,鎖的類型也不一樣,對於innodb

5、mysql的存儲引擎有哪幾種?區別和各自的適用場景。

6、query cache的配置

7、ACID

原子性、一致性、隔離性、持久性。

8、如何優化慢查詢

答:1.查詢條件帶上索引,

9、最左前綴匹配原則,原理

四、算法

1、一致性哈希的原理

2、手寫二分查找,快速排序

3、手寫LRU算法

4、兩個鏈表找交點

5、兩個無限長的數字求和

6、手寫生產者消費者demo

7、256M內存排序2G大小的文件

8、求數組最大子序列

五、操作系統與計算機網絡

1、如何從訪問日誌中找出量最大的10個ip?awk語句了解嗎?

2、jstack,jstat,jmap,jheap命令了解嗎,如何使用?

3、系統負載情況如何查看?

4、網絡分層協議了解嗎?

5、tcp三次握手,四次揮手了解嗎?

6、aio,bio,nio的區別

7、select,poll,epoll的區別?

8、io模型有哪些?

六、開源框架與組件

這部分主要根據簡歷以及項目的實際情況來問。

1、對spring了解嗎?ioc,aop,transaction註解

2、spingmvc了解嗎?

3、Redis與memcache的區別

4、redis持久化策略,rdb與aof的區別與應用場景

5、memcached的內存是如何分配的?一致性哈希原理

6、mq的原理與應用場景,mq是如何保證不丟消息的?

7、tomcat的原理,主要運用了哪些設計模式?

8、redis與memcached內存分別是如何回收的?

9、guava的緩存是怎麽實現的?

七、場景設計與架構

1、秒殺場景,如何做技術選型?

2、設計一個支持高並發的服務,寫出核心代碼

3、高並發與高可用如何實現?

4、服務降級怎麽做?限流、限速、超時重試、熔斷、自恢復、分別如何實現?

5、什麽是微服務?有什麽好處?為什麽要這麽做?

6、CAP理論是什麽?項目中的哪些場景用到了CAP理論?

7、BASE理論是什麽?

8、什麽時候應該使用mq?

八、其他

1、平時都通過什麽方式學習技術?

2、最近學的一個知識點是什麽?

3、對帶人有什麽經驗?

4、最熟悉的一個項目是什麽?

5、跳槽的時候,你最看重什麽?

6、為什麽跳槽?為什麽選擇我們公司?

聊聊基礎