java高級面試題總結

目錄

1、java基礎

1.1、hashmap原理?擴容

HashMap基於hashing原理,我們通過put()和get()方法儲存和獲取對象。當我們將鍵值對傳遞給put()方法時,它調用鍵對象的hashCode()方法來計算hashcode,找到bucket位置來儲存鍵值對對象。當獲取對象時,通過鍵對象的equals()方法找到正確的鍵值對,然後返回值對象。HashMap使用鏈表來解決碰撞問題,當發生碰撞了,對象將會儲存在鏈表的下一個節點中。 HashMap在每個鏈表節點中儲存鍵值對對象。
當兩個不同的鍵對象的hashcode相同時會發生什麼? 它們會儲存在同一個bucket位置的鏈表中。鍵對象的equals()方法用來找到鍵值對。
https://blog.csdn.net/kun_998/article/details/89480637

1.2、arraylist原理?擴容

ArrayList的擴容主要發生在向ArrayList集合中添加元素的時候。由add()方法的分析可知添加前必須確保集合的容量能夠放下添加的元素。主要經歷了以下幾個階段:

第一,在add()方法中調用ensureCapacityInternal(size + 1)方法來確定集合確保添加元素成功的最小集合容量minCapacity的值。參數爲size+1,代表的含義是如果集合添加元素成功後,集合中的實際元素個數。換句話說,集合爲了確保添加元素成功,那麼集合的最小容量minCapacity應該是size+1。在ensureCapacityInternal方法中,首先判斷elementData是否爲默認的空數組,如果是,minCapacity爲minCapacity與集合默認容量大小中的較大值。

第二,調用ensureExplicitCapacity(minCapacity)方法來確定集合爲了確保添加元素成功是否需要對現有的元素數組進行擴容。首先將結構性修改計數器加一;然後判斷minCapacity與當前元素數組的長度的大小,如果minCapacity比當前元素數組的長度的大小大的時候需要擴容,進入第三階段。

第三,如果需要對現有的元素數組進行擴容,則調用grow(minCapacity)方法,參數minCapacity表示集合爲了確保添加元素成功的最小容量。在擴容的時候,首先將原元素數組的長度增大1.5倍(oldCapacity + (oldCapacity >> 1)),然後對擴容後的容量與minCapacity進行比較:① 新容量小於minCapacity,則將新容量設爲minCapacity;②新容量大於minCapacity,則指定新容量。最後將舊數組拷貝到擴容後的新數組中。
arrays.copyof()方法,返回一個新數組。
https://blog.csdn.net/aizhuyanwei/article/details/78493495
https://blog.csdn.net/weixin_36378917/article/details/81812210

1.3、jdk1.8新特性?

https://blog.csdn.net/qq_29411737/article/details/80835658

1.4、completablefuture

https://www.jianshu.com/p/6bac52527ca4

1.5、Integer用==進行值比較,什麼時候相等,什麼時候不等?

在值域爲 [-128,127]之間,用==符號來比較Integer的值,是相等的。
在Integer類裝入內存中時,會執行其內部類中靜態代碼塊進行其初始化工作,做的主要工作就是把 [-128,127]之間的數包裝成Integer類並把其對應的引用存入到cache數組中,這樣在方法區中開闢空間存放這些靜態Integer變量,同時靜態cache數組也存放在這裏,供線程享用,這也稱靜態緩存。我們知道在Java的對象是引用的,所以當用Integer 聲明初始化變量時,會先判斷所賦值的大小是否在-128到127之間,若在,則利用靜態緩存中的空間並且返回對應cache數組中對應引用,存放到運行棧中,而不再重新開闢內存。

1.6 ==和equals()的區別?

1, = = 是一個java的運算符,比較的是等號兩邊的地址值是否相等。equals()是一個object類的方法,具體要看類的實現。
2,基本數據類型在JVM的分佈實在虛擬機棧的中的局部變量表裏,使用= =和equals()比較基本數據類型是一樣的效果。
3,string類重寫了equals()方法,比較的是string的值是不是相等,具體實現是通過遍歷char數組每個元素對比。

int i = 11;
int j = 11;
i == j        //true
i.equels(j)     //基本數據類型沒有equals方法
--------------------------------------------------
Integer i = 11;
Integer j = 11;
i == j          // true
i.equels(j)       // true
------------------------------------------------
 String a = new String ("1");
 String b = new String ("1");
 a == b      //false
 a.equelse(b)     // true
 -----------------------------------------
 String a ="1";
 String b = "1";
 a == b     // true
 a.equelse(b)     // true

1.7、java IO流中有哪些?轉換流有哪些?

字節流:OutputStream InputStream 字節緩衝流:BufferedOutputStream BufferedInputStream
字符流:Writer Reader 字符緩衝流:BufferedWriter BufferedReader
轉換流:InputStreamReader:字節流轉換爲字符流
OutputStreamWriter:字符流轉換爲字節流

1.8、java NIO

java NIO是在jdk1.4加入的,爲了解決原來javaIO的阻塞問題,可以理解爲New IO 護着NoBlockIO。
Java NIO 由以下幾個核心部分組成:
Channels
Buffers
Selectors
新的輸入/輸出 (NIO) 庫是在 JDK 1.4 中引入的。NIO 彌補了原來的 I/O 的不足,它在標準 Java 代碼中提供了高速的、面向塊的 I/O。通過定義包含數據的類,以及通過以塊的形式處理這些數據,NIO 不用使用本機代碼就可以利用低級優化,這是原來的 I/O 包所無法做到的。
https://ifeve.com/overview/

1.8 hashmap和hashset的關係和區別

關係:
hashset是由hashmap實現的
區別:

HashMap HashSet
HashMap實現了Map接口 HashSet實現了Set接口
HashMap儲存鍵值對 HashSet僅僅存儲對象
使用put()方法將元素放入map中 使用add()方法將元素放入set中
HashMap中使用鍵對象來計算hashcode值 HashSet使用成員對象來計算hashcode值,對於兩個對象來說hashcode可能相同,所以equals()方法用來判斷對象的相等性,如果兩個對象不同的話,那麼返回false
HashMap比較快,因爲是使用唯一的鍵來獲取對象 HashSet較HashMap來說比較慢

1.9 傳統單例模式雙重檢查鎖存在的問題

https://www.cnblogs.com/a154627/p/10046147.html

2、數據庫

2.1、mysql索引優化

https://www.cnblogs.com/qlqwjy/p/8592043.html

2.2、創建索引的依據?

1、表的主鍵、外鍵必須有索引;
2、數據量超過300的表應該有索引;
3、經常與其他表進行連接的表,在連接字段上應該建立索引;
4、經常出現在Where子句中的字段,特別是大表的字段,應該建立索引;
5、索引應該建在選擇性高的字段上;
6、索引應該建在小字段上,對於大的文本字段甚至超長字段,不要建索引;
7、複合索引的建立需要進行仔細分析;儘量考慮用單字段索引代替:
A、正確選擇複合索引中的主列字段,一般是選擇性較好的字段;
B、複合索引的幾個字段是否經常同時以AND方式出現在Where子句中?單字段查詢是否極少甚至沒有?
如果是,則可以建立複合索引;否則考慮單字段索引;
C、如果複合索引中包含的字段經常單獨出現在Where子句中,則分解爲多個單字段索引;
D、如果複合索引所包含的字段超過3個,那麼仔細考慮其必要性,考慮減少複合的字段;
E、如果既有單字段索引,又有這幾個字段上的複合索引,一般可以刪除複合索引;
8、頻繁進行數據操作的表,不要建立太多的索引;
9、刪除無用的索引,避免對執行計劃造成負面影響;

2.3、mysql執行計劃,explain各項參數代表什麼意思?

id:選擇標識符
select_type:表示查詢的類型。
table:輸出結果集的表
partitions:匹配的分區
type:表示表的連接類型
possible_keys:表示查詢時,可能使用的索引
key:表示實際使用的索引
key_len:索引字段的長度
ref:列與索引的比較
rows:掃描出的行數(估算的行數)
filtered:按表條件過濾的行百分比
Extra:執行情況的描述和說明

注意:type標識mysql的訪問類型,常見的有ALL、index、range、 ref、eq_ref、const、system、NULL(從左到右,性能從差到好)
ALL:Full Table Scan, MySQL將遍歷全表以找到匹配的行
index: Full Index Scan,index與ALL區別爲index類型只遍歷索引樹
range:只檢索給定範圍的行,使用一個索引來選擇行
ref: 表示上述表的連接匹配條件,即哪些列或常量被用於查找索引列上的值
eq_ref: 類似ref,區別就在使用的索引是唯一索引,對於每個索引鍵值,表中只有一條記錄匹配,簡單來說,就是多表連接中使用primary key或者 unique key作爲關聯條件
const、system: 當MySQL對查詢某部分進行優化,並轉換爲一個常量時,使用這些類型訪問。如將主鍵置於where列表中,MySQL就能將該查詢轉換爲一個常量,system是const類型的特例,當查詢的表只有一行的情況下,使用system
NULL: MySQL在優化過程中分解語句,執行時甚至不用訪問表或索引,例如從一個索引列裏選取最小值可以通過單獨索引查找完成。

2.4、什麼時候不會用到索引?

索引列上不能使用表達式或函數
用or分割開的條件,如果or左右兩個條件中有一個列沒有索引,則不會使用索引。
複合索引,如果索引列不是複合索引的第一部分,則不使用索引(即不符合最左前綴)
如果like是以‘%’開始的,則該列上的索引不會被使用。
如果MySQL估計使用索引比全表掃描更慢,則不適用索引,
如果列爲字符串,則where條件中必須將字符常量值加引號,否則即使該列上存在索引,也不會被使用

2.5、mysql存儲引擎

https://www.cnblogs.com/xiaohaillong/p/6079551.html

2.6、如何修改大表的表結構(增加一列)

在主服務器上建立新表,表結構爲修改後的表結構,將舊錶數據同步到新表。
在舊錶中建立觸發器,使舊錶更新的數據同步到新表中。
數據同步完成後,在舊錶中建立排它鎖,重命名新表爲舊錶,刪除舊錶。
可使用工具實現上面的操作:pt-online-schema-change

2.7、數據庫隔離級別

Mysql查看當前隔離級別:show variables like ‘%iso%’

隔離級別 隔離級別的值 導致的問題
Read-Uncommitted 0 導致髒讀
Read-Committed 1 避免髒讀,允許不可重複讀和幻讀
Repeatable-Read 2 避免髒讀,不可重複讀,允許幻讀
Serializable 3 串行 化讀,事務只能一個一個執行,避免了髒讀、不可重複讀、幻讀。執行效率慢,使用時慎重
髒讀:一事務對數據進行了增刪改,但未提交,另一事務可以讀取到未提交的數據。如果第一個事務這時候回滾了,那麼第二個事務就讀到了髒數據。
不可重複讀:一個事務中發生了兩次讀操作,第一次讀操作和第二次操作之間,另外一個事務對數據進行了修改,這時候兩次讀取的數據是不一致的。
幻讀:第一個事務對一定範圍的數據進行批量修改,第二個事務在這個範圍增加一條數據,這時候第一個事務就會丟失對新增數據的修改。
總結:
隔離級別越高,越能保證數據的完整性和一致性,但是對併發性能的影響也越大。
大多數的數據庫默認隔離級別爲 Read Commited,比如 SqlServer、Oracle
少數數據庫默認隔離級別爲:Repeatable Read 比如: MySQL InnoDB

2.7、爲什麼要使用索引

索引大大減少存儲引擎需要掃描的數據量
索引可以幫助我們進項排序避免使用臨時表
索引可以把隨機io變爲順序io

2.8、聯合索引什麼時候回用到,什麼時候不會用到?

最左匹配原則:b+樹的數據項是複合的數據結構,比如(name,age,sex)的時候,b+數是按照從左到右的順序來建立搜索樹的,比如當(張三,20,F)這樣的數據來檢索的時候,b+樹會優先比較name來確定下一步的所搜方向,如果name相同再依次比較age和sex,最後得到檢索的數據;但當(20,F)這樣的沒有name的數據來的時候,b+樹就不知道下一步該查哪個節點,因爲建立搜索樹的時候name就是第一個比較因子,必須要先根據name來搜索才能知道下一步去哪裏查詢。比如當(張三,F)這樣的數據來檢索時,b+樹可以用name來指定搜索方向,但下一個字段age的缺失,所以只能把名字等於張三的數據都找到,然後再匹配性別是F的數據了, 這個是非常重要的性質,即索引的最左匹配特性。

2.9、mysql的limit用法?

SELECT * FROM table LIMIT 5,10; // 檢索記錄行 6-15
SELECT * FROM table LIMIT 10; // 檢索前10條記錄

2.10、mysql的select 、where、orderby、having的 執行順序?having的用法?

邏輯查詢處理階段簡介

  1. FROM:對FROM子句中的前兩個表執行笛卡爾積(Cartesian product)(交叉聯接),生成虛擬表VT1
  2. ON:對VT1應用ON篩選器。只有那些使<join_condition>爲真的行才被插入VT2。
  3. OUTER(JOIN):如 果指定了OUTER JOIN(相對於CROSS JOIN 或(INNER JOIN),保留表(preserved table:左外部聯接把左表標記爲保留表,右外 部 聯接把右表標記爲保留表,完全外部聯接把兩個表都標記爲保留表)中未找到匹配的行將作爲外部行添加到 VT2,生成VT3.如果FROM子句包含 兩個 以上的表,則對上一個聯接生成的結果表和下一個表重複執行步驟1到步驟3,直到處理完所有的表爲止。
  4. WHERE:對VT3應用WHERE篩選器。只有使<where_condition>爲true的行才被插入VT4.
  5. GROUP BY:按GROUP BY子句中的列列表對VT4中的行分組,生成VT5.
  6. CUBE|ROLLUP:把超組(Suppergroups)插入VT5,生成VT6.
  7. HAVING:對VT6應用HAVING篩選器。只有使<having_condition>爲true的組纔會被插入VT7.
  8. SELECT:處理SELECT列表,產生VT8.
  9. DISTINCT:將重複的行從VT8中移除,產生VT9.
  10. ORDER BY:將VT9中的行按ORDER BY 子句中的列列表排序,生成遊標(VC10).
  11. TOP:從VC10的開始處選擇指定數量或比例的行,生成表VT11,並返回調用者。

2.9、mysql的存儲引擎的區別?

主要比較InnoDB和MYISAM
InnoDB支持事務,MyISAM不支持,對於InnoDB每一條SQL語言都默認封裝成事務,自動提交,這樣會影響速度,所以最好把多條SQL語言放在begin和commit之間,組成一個事務;
InnoDB是聚集索引,數據文件是和索引綁在一起的,必須要有主鍵,通過主鍵索引效率很高。但是輔助索引需要兩次查詢,先查詢到主鍵,然後再通過主鍵查詢到數據。因此,主鍵不應該過大,因爲主鍵太大,其他索引也都會很大。MyISAM是非聚集索引,數據文件是分離的,索引保存的是數據文件的指針。主鍵索引和輔助索引是獨立的。
InnoDB不保存表的具體行數,執行select count(*) from table時需要全表掃描。而MyISAM用一個變量保存了整個表的行數,執行上述語句時只需要讀出該變量即可,速度很快;
Innodb不支持全文索引,而MyISAM支持全文索引,查詢效率上MyISAM要高;

3、java多線程

3.1、java併發包java.util.concurrent及其子包都包括什麼?

提供了比synchronized更加高級的各種同步結構,包括CountDownLatch、CyclicBarrier、Semaphore等,可以實現更加豐富的多線程操作。比如利用Semaphore作爲資源控制器,限制同時進行工作的線程數量。
各種線程安全的容器,比如最常見的ConcurrentHashMap、有序的ConcurrentSkipListMap,或者通過類似快照機制,實現線程安全的動態數組CopyOnWriteArrayList等。
各種併發隊列實現,如各種BlockingQueue實現,比較典型的ArrayBlockingQueue、SynchorousQueue或針對特定場景的PriorityBlockingQueue等。
強大的Executor框架,可以創建各種不同類型的線程池,調度任務運行等。絕大部分情況下,不再需要自己從頭實現線程池和任務調度器。

https://www.jianshu.com/p/850e3282a80a

3.2、synconsized和volatile關鍵字區別?

volatile本質是在告訴jvm當前變量在寄存器中的值是不確定的,需要從主存中讀取,synchronized則是鎖定當前變量,只有當前線程可以訪問該變量,其他線程被阻塞住.
volatile僅能使用在變量級別,synchronized則可以使用在變量,方法.
volatile僅能實現變量的修改可見性,但不具備原子特性,而synchronized則可以保證變量的修改可見性和原子性.
volatile不會造成線程的阻塞,而synchronized可能會造成線程的阻塞.
volatile標記的變量不會被編譯器優化,而synchronized標記的變量可以被編譯器優化.

3.3、實現線程池的方式?

阿里的 Java開發手冊,上面有線程池的一個建議:
線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,
這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。
參考

3.4、公平鎖與非公平鎖

https://www.jianshu.com/p/eaea337c5e5b

3.5、爲什麼不適用Excutors來創建線程池

https://blog.csdn.net/weixin_41888813/article/details/90769126

3.6、ReentraneLock & AQS

ReentranceLock可重如鎖
https://www.cnblogs.com/binbang/p/6425248.html
https://www.jianshu.com/p/da9d051dcc3d

3.7、手寫線程死鎖

public class TestDeadLock {

    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    public static void main(String[] args) {
        deaLock();
    }

    private static void deaLock(){
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                synchronized (lock1){
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (lock2){
                        System.out.println("Thread1執行print方法");
                    }
                }
            }
        });
        thread1.setName("thread1");
        thread1.start();
        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                synchronized (lock2){
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (lock1){
                        System.out.println("Thread2執行print方法");
                    }
                }
            }
        });
        thread2.setName("thread2");
        thread2.start();
    }
}

4、jvm、java內存模型

4.1、jvm內存模型?

https://www.nowcoder.com/discuss/151138?type=1

4.2、jvm調優具體調的那些參數?

|Xms| 初始堆大小,也可稱最小堆大小 |
|-Xmx | 最大堆大小 |
|-Xmn|堆中新生代的大小 |
|-Xss | 每個線程的堆棧大小|
|-XX:PermSize| 設置持久代(perm gen)初始值 |
|-XX:MaxPermSize | 設置持久代最大值 |

5、java框架(spring boot,sprint cloud)

5.1、mybatis一級緩存,二級緩存

1)一級緩存 Mybatis的一級緩存是指SQLSession,一級緩存的作用域是SQlSession, Mabits默認開啓一級緩存。 在同一個SqlSession中,執行相同的SQL查詢時;第一次會去查詢數據庫,並寫在緩存中,第二次會直接從緩存中取。 當執行SQL時候兩次查詢中間發生了增刪改的操作,則SQLSession的緩存會被清空。 每次查詢會先去緩存中找,如果找不到,再去數據庫查詢,然後把結果寫到緩存中。 Mybatis的內部緩存使用一個HashMap,key爲hashcode+statementId+sql語句。Value爲查詢出來的結果集映射成的java對象。 SqlSession執行insert、update、delete等操作commit後會清空該SQLSession緩存。
2)二級緩存 二級緩存是mapper級別的,Mybatis默認是沒有開啓二級緩存的。 第一次調用mapper下的SQL去查詢用戶的信息,查詢到的信息會存放代該mapper對應的二級緩存區域。 第二次調用namespace下的mapper映射文件中,相同的sql去查詢用戶信息,會去對應的二級緩存內取結果。 如果調用相同namespace下的mapepr映射文件中增刪改sql,並執行了commit操作,此時會清空該緩存

5.2、mybatis ${}和#{}的區別?

#{}的執行結果是給參數添加‘’,
美元符{}的執行結果是直接將參數替換,存在sql注入的風險。
能用#{}的地方就不要用${}

5.3、mybatis中like模糊查詢中#和$的使用

如果在like語句中想使用#{},通過字符串拼接或者concat拼接的方式實現
1、表達式: name like"%"#{name}"%"
2、name like concat(concat(’%’,#{username}),’%’)
concat拼接可以這麼寫name like CONCAT(’%’,’${name}’,’%’)
https://blog.csdn.net/u010398771/article/details/70768280

5.3、sprint cloud 的常用組件?

Spring Cloud Eureka(服務治理)
Spring Cloud Ribbon(客戶端負載均衡)
Spring Cloud Hystrix(服務容錯保護)
Spring Cloud Feign(聲明式服務調用)
Spring Cloud Zuul(API網關服務)
Spring Cloud Config(分佈式配置中心)

springcloud相關面試題

https://blog.csdn.net/panhaigang123/article/details/79587612?utm_source=distribute.pc_relevant.none-task

6、中間件

6.1、Redis面試

https://blog.csdn.net/Butterfly_resting/article/details/89668661

6.2、redis實現分佈式鎖原理,使用redis實現分佈式鎖有什麼問題?

https://www.cnblogs.com/gxyandwmm/p/9588383.html

6.3、zookeeper如何實現公平鎖

獲取鎖:首先在zookeeper當中創建一個持久節點ParentLock。當第一個客戶端client1想要獲取鎖時,需要在ParentLock這個節點下面創建一個臨時順序節點Lock1,之後client1查號ParentLock下面的所有臨時順序節點並排序,判斷自己所創建的加點lock1是不是順序最靠前的一個,如果是,則獲取鎖成功。這時候另一個客戶端client2來獲取鎖,則在ParentLock下在創建一個臨時順序節點lock2,client2查找ParentLock下所有的臨時順序節點並排序,判斷自己創建的節點lock2是不是順序節點裏最靠前的一個,發現lock2不是最小的,於是cline2向排序比他靠前的節點lock1註冊Watch,用於監聽lock1節點是否存在,這意味着clinet2搶鎖失敗,進入等待狀態。這時候又有一個客戶端client3前來獲取鎖,同樣在ParentLock下創建臨時順序節點lock3,查找所有順序節點並排序,判斷自己創建的節點lock3是不是最靠前的一個,同樣發現lock3不是最小的,於是client3向排序僅比他靠前的節點lock2註冊watch,用於監聽lock2節點是否存在,同樣意味着clinet3搶鎖失敗,進入等待狀態。這樣一來,clinet1得到了鎖,client2監聽了client1,clinet3監聽了clinet2,恰好行程一個等待隊列。
釋放鎖:釋放鎖可以分爲兩種情況
當client1任務完成時,會顯式的調用少出節點Lock1的指令來釋放鎖
當任務執行過程中,客戶端奔潰,則client1會斷開與zookeeper的連接,根據臨時節點的特新,Lock1節點也會隨之自動刪除。由於client2一直監聽這lock1的狀態,當lock1節點刪除後,client2會立刻受到通知,這時候client2在查詢ParentLock下的所有節點,確認自己創建的節點lock2是不是最小的節點,如果是,則clinet2獲取鎖。

6.4、如何訪問 redis 中的海量數據?避免事故產生

那我們如何去遍歷大數據量呢?這個也是面試經常問的。我們可以採用redis的另一個命令scan。我們看一下scan的特點
1、複雜度雖然也是 O(n),但是它是通過遊標分步進行的,不會阻塞線程
2、提供 count 參數,不是結果數量,是redis單次遍歷字典槽位數量(約等於)
3、同 keys 一樣,它也提供模式匹配功能;
4、服務器不需要爲遊標保存狀態,遊標的唯一狀態就是 scan 返回給客戶端的遊標整數;
5、返回的結果可能會有重複,需要客戶端去重複,這點非常重要;
6、單次返回的結果是空的並不意味着遍歷結束,而要看返回的遊標值是否爲零。
一、scan命令格式
SCAN cursor [MATCH pattern] [COUNT count]
二、命令解釋:scan 遊標 MATCH <返回和給定模式相匹配的元素> count 每次迭代所返回的元素數量
SCAN命令是增量的循環,每次調用只會返回一小部分的元素。所以不會讓redis假死SCAN命令返回的是一個遊標,從0開始遍歷,到0結束遍歷
三、舉例
redis > scan 0match user_token* count 5

  1. “6”
    1. “user_token:1000”
  2. “user_token:1001”
  3. “user_token:1010”
  4. “user_token:2300”
  5. “user_token:1389”
    從0開始遍歷,返回了遊標6,又返回了數據,繼續scan遍歷,就要從6開始
    redis > scan 6match user_token* count 5
  6. “10”
    1. “user_token:3100”
  7. “user_token:1201”
  8. “user_token:1410”
  9. “user_token:5300”
  10. “user_token:3389”
    http://www.sohu.com/a/335031964_355142

6.5、rabbitmq如何實現消息的不丟失?

https://www.cnblogs.com/cnndevelop/p/12091348.html

6.6、dubbo原理

  1. client一個線程調用遠程接口,生成一個唯一的ID(比如一段隨機字符串,UUID等),Dubbo是使用AtomicLong從0開始累計數字的
  2. 將打包的方法調用信息(如調用的接口名稱,方法名稱,參數值列表等),和處理結果的回調對象callback,全部封裝在一起,組成一個對象object
  3. 向專門存放調用信息的全局ConcurrentHashMap裏面put(ID, object)
  4. 將ID和打包的方法調用信息封裝成一對象connRequest,使用IoSession.write(connRequest)異步發送出去
  5. 當前線程再使用callback的get()方法試圖獲取遠程返回的結果,在get()內部,則使用synchronized獲取回調對象callback的鎖, 再先檢測是否已經獲取到結果,如果沒有,然後調用callback的wait()方法,釋放callback上的鎖,讓當前線程處於等待狀態。
  6. 服務端接收到請求並處理後,將結果(此結果中包含了前面的ID,即回傳)發送給客戶端,客戶端socket連接上專門監聽消息的線程收到消息,分析結果,取到ID,再從前面的ConcurrentHashMap裏面get(ID),從而找到callback,將方法調用結果設置到callback對象裏。
  7. 監聽線程接着使用synchronized獲取回調對象callback的鎖(因爲前面調用過wait(),那個線程已釋放callback的鎖了),再notifyAll(),喚醒前面處於等待狀態的線程繼續執行(callback的get()方法繼續執行就能拿到調用結果了),至此,整個過程結束。
    需要注意的是,這裏的callback對象是每次調用產生一個新的,不能共享,否則會有問題;另外ID必需至少保證在一個Socket連接裏面是唯一的。
    兩個問題
    · 當前線程怎麼讓它“暫停”,等結果回來後,再向後執行?
    答:先生成一個對象obj,在一個全局map裏put(ID,obj)存放起來,再用synchronized獲取obj鎖,再調用obj.wait()讓當前線程處於等待狀態,然後另一消息監聽線程等到服務端結果來了後,再map.get(ID)找到obj,再用synchronized獲取obj鎖,再調用obj.notifyAll()喚醒前面處於等待狀態的線程。
    · 正如前面所說,Socket通信是一個全雙工的方式,如果有多個線程同時進行遠程方法調用,這時建立在client server之間的socket連接上會有很多雙方發送的消息傳遞,前後順序也可能是亂七八糟的,server處理完結果後,將結果消息發送給client,client收到很多消息,怎麼知道哪個消息結果是原先哪個線程調用的?
    答:使用一個ID,讓其唯一,然後傳遞給服務端,再服務端又回傳回來,這樣就知道結果是原先哪個線程的了。

6.7、Redis的幾種數據結構及其使用場景

string介紹
string是以一種純字符串作爲value的形式存在的。也是這幾種之中使用最多的數據結構。value可以存儲json格式、數值型等。
string使用場景
string使用場景一般是存儲簡單的鍵值類型。比如用戶信息,登錄信息,配置信息等。還有一種用得比較多的是string的incr/decr操作,即自減/自增操作。調用它是原子性的,無論調用多少次,都一一計算成功。例如需要增減庫存的操作。
儘管string的value可以存儲很大,甚至500多MB的容量。但是在性能上來說,我們儘量存儲value的值不要過1MB。
hash介紹
hash是一個集合,使用過hash的人都知道,hash的讀取性能都一樣快。在redis中,hash因爲是一個集合,所以有兩層。第一層是key:hash集合value,第二層是hashkey:string value。所以判斷是否採用hash的時候可以參照有兩層key的設計來做參考。並且注意的是,設置過期時間只能在第一層的key上面設置。
hash用場景
使用hash,一般是有那種需要兩層key的應用場景,也可以是‘刪除一個key可以刪除所有內容’的場景。例如一個商品有很多規格,規格里面有不同的值。
如果需要刪除商品時,可以一次性刪除‘商品id’的key,則商品裏面的所有規格也會刪除,而不需要找到對應的規格再做處理。如果查找商品id與規格id1的商品時,則通過兩個key查找即可。或者查找所有商品的規格,查找商品id即可。
需要注意的是,經過測試,在性能上來說一般hash裏面的第二層key,不要超過200個爲佳。儘管hash裏面的key-value能達到500多MB的存儲容量。
list介紹
list是一個集合,而在redis中,list的使用場景多種多樣。在redis中,插入list中的值,只需要找到list的key即可,而不需要像hash一樣插入兩層的key。list是一種有序的、可重複的集合。
list使用場景
list可以使用左推、左拉、右推、右拉的方式。所以你可以使用list作爲集合存儲,比如存儲某寶商鋪裏面的所有商品。
也可以用作輕量級別的隊列來使用。左推左拉、右推右拉。
需要注意的是儘管redis可以使用推拉的隊列模式,但是一定要注意場景。因爲redis的隊列是一種輕量級別的,沒有隊列重試、隊列重放機制。消費完隊列消息在redis代表已經刪除了。
set介紹
set是一種無序的,不能重複的集合。並且在redis中,只有一個key。
set使用場景
如保存一些標籤的名字。標籤的名字不可以重複,順序是可以無序的。
需要注意的是使用set一定不要存儲大量的數據。value的值不宜過大,並且集合數量不宜過大。幾百個集合的值,value不超過1MB爲佳。
sortset介紹
sortset在redis中是有序的,並且不能重複。既有list的有序,又有set的不可重複性。
sortset使用場景
sortset的使用場景一般是排行榜之類的場景。
https://www.cnblogs.com/alunchen/p/9836170.html

6.8、redis的zset數據結構

Set數據結構類似於Set結構,只是ZSet結構中,每個元素都會有一個分值,然後所有元素按照分值的大小進行排列,相當於是一個進行了排序的鏈表。
如果ZSet是一個鏈表,而且內部元素是有序的,在進行元素插入和刪除,以及查詢的時候,就必須要遍歷鏈表纔行,時間複雜度就達到了O(n),這個在以單線程處理的Redis中是不能接受的。所以ZSet採用了一種跳躍表的實現。
跳躍表因爲是一個根據分數權重進行排序的列表,可以再很多場景中進行應用,比如排行榜等等。
https://www.cnblogs.com/wuyizuokan/p/11108417.html

7、算法與數據結構

7.1、快排算法(手寫)

public class QuickSort {
	public static void main(String[] args) {
		int[] arr = { 49, 38, 65, 97, 23, 22, 76, 1, 5, 8, 2, 0, -1, 22 };
		quickSort(arr, 0, arr.length - 1);
		System.out.println("排序後:");
		for (int i : arr) {
			System.out.println(i);
		}
	}

	private static void quickSort(int[] arr, int low, int high) {
		if (low < high) {
			// 找尋基準數據的正確索引
			int index = getIndex(arr, low, high);
			quickSort(arr, 0, index - 1);
			quickSort(arr, index + 1, high);
		}
	}

	private static int getIndex(int[] arr, int low, int high) {
		int tmp = arr[low];
		while (low < high) {
			while (low < high && arr[high] >= tmp) {
				high--;
			}
			arr[low] = arr[high];
			while (low < high && arr[low] <= tmp) {
				low++;
			}
			arr[high] = arr[low];
		}
		arr[low] = tmp;
		return low; 
	}
}

7.2、鏈表反轉

7.3、刪除鏈表最後第n個值

7.4、二分查找

7.5、樹,紅黑樹,b+樹

7.6、手寫一個隊列

https://msd.misuland.com/pd/9786112860861905

8、網絡

8.1、tcp,udp,http區別

8.2、三次握手,四次揮手

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章