欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java經(jīng)典面試題最全匯總208道(六)

 更新時(shí)間:2023年01月17日 17:25:45   作者:哪?吒  
這篇文章主要介紹了Java經(jīng)典面試題最全匯總208道(六),本文章內(nèi)容詳細(xì),該模塊分為了六個(gè)部分,本次為第六部分,需要的朋友可以參考下

前言 

短時(shí)間提升自己最快的手段就是背面試題,最近總結(jié)了Java常用的面試題,分享給大家,希望大家都能圓夢(mèng)大廠,加油,我命由我不由天。

181、什么是類(lèi)加載器,類(lèi)加載器有哪些?

1、什么是類(lèi)加載器?

類(lèi)加載器負(fù)責(zé)加載所有的類(lèi),其為所有被載入內(nèi)存的類(lèi)生成一個(gè)java.lang.Class實(shí)例對(duì)象。

2、類(lèi)加載器有哪些?

JVM有三種類(lèi)加載器:

(1)啟動(dòng)類(lèi)加載器

該類(lèi)沒(méi)有父加載器,用來(lái)加載Java的核心類(lèi),啟動(dòng)類(lèi)加載器的實(shí)現(xiàn)依賴(lài)于底層操作系統(tǒng),屬于虛擬機(jī)實(shí)現(xiàn)的一部分,它并不繼承自java.lang.classLoader。

(2)擴(kuò)展類(lèi)加載器

它的父類(lèi)為啟動(dòng)類(lèi)加載器,擴(kuò)展類(lèi)加載器是純java類(lèi),是ClassLoader類(lèi)的子類(lèi),負(fù)責(zé)加載JRE的擴(kuò)展目錄。

(3)應(yīng)用程序類(lèi)加載器

它的父類(lèi)為擴(kuò)展類(lèi)加載器,它從環(huán)境變量classpath或者系統(tǒng)屬性java.lang.path所指定的目錄中加載類(lèi),它是自定義的類(lèi)加載器的父加載器。

182、說(shuō)一下類(lèi)加載的執(zhí)行過(guò)程?

當(dāng)程序主動(dòng)使用某個(gè)類(lèi)時(shí),如果該類(lèi)還未被加載到內(nèi)存中,JVM會(huì)通過(guò)加載、連接、初始化3個(gè)步驟對(duì)該類(lèi)進(jìn)行類(lèi)加載。

1、加載

加載指的是將類(lèi)的class文件讀入到內(nèi)存中,并為之創(chuàng)建一個(gè)java.lang.Class對(duì)象。

類(lèi)的加載由類(lèi)加載器完成,類(lèi)加載器由JVM提供,開(kāi)發(fā)者也可以通過(guò)繼承ClassLoader基類(lèi)來(lái)創(chuàng)建自己的類(lèi)加載器。

通過(guò)使用不同的類(lèi)加載器可以從不同來(lái)源加載類(lèi)的二進(jìn)制數(shù)據(jù),通常有如下幾種來(lái)源:

從本地文件系統(tǒng)加載從jar包加載通過(guò)網(wǎng)絡(luò)加載把一個(gè)Java源文件動(dòng)態(tài)編譯,并執(zhí)行加載

2、連接

當(dāng)類(lèi)被加載之后,系統(tǒng)為之生成一個(gè)對(duì)應(yīng)的Class對(duì)象,接著進(jìn)入連接階段,連接階段負(fù)責(zé)將類(lèi)的二進(jìn)制數(shù)據(jù)合并到JRE中。

類(lèi)連接又可分為三個(gè)階段:

(1)驗(yàn)證

  • 文件格式驗(yàn)證
  • 元數(shù)據(jù)驗(yàn)證
  • 字節(jié)碼驗(yàn)證
  • 符號(hào)引用驗(yàn)證

(2)準(zhǔn)備

為類(lèi)的靜態(tài)變量分配內(nèi)存,并設(shè)置默認(rèn)初始值。

(3)解析

將類(lèi)的二進(jìn)制數(shù)據(jù)中的符號(hào)引用替換成直接引用。

3、初始化

為類(lèi)的靜態(tài)變量賦予初始值。

183、JVM的類(lèi)加載機(jī)制是什么?

JVM類(lèi)加載機(jī)制主要有三種:

1、全盤(pán)負(fù)責(zé)

類(lèi)加載器加載某個(gè)class時(shí),該class所依賴(lài)的和引用其它的class也由該類(lèi)加載器載入。

2、雙親委派

先讓父加載器加載該class,父加載器無(wú)法加載時(shí)才考慮自己加載。

3、緩存機(jī)制

緩存機(jī)制保證所有加載過(guò)的class都會(huì)被緩存,當(dāng)程序中需要某個(gè)class時(shí),先從緩存區(qū)中搜索,如果不存在,才會(huì)讀取該類(lèi)對(duì)應(yīng)的二進(jìn)制數(shù)據(jù),并將其轉(zhuǎn)換成class對(duì)象,存入緩存區(qū)中。

這就是為什么修改了class后,必須重啟JVM,程序所做的修改才會(huì)生效的原因。

184、什么是雙親委派模型?

如果一個(gè)類(lèi)收到了類(lèi)加載請(qǐng)求,它并不會(huì)自己先去加載,而是把這個(gè)請(qǐng)求委托給父類(lèi)的加載器執(zhí)行

如果父加載器還存在其父加載器,則進(jìn)一步向上委托,依次遞歸,請(qǐng)求將最終到達(dá)頂層的啟動(dòng)類(lèi)加載器

如果父類(lèi)加載器可以完成父加載任務(wù),就成功返回,如果父加載器無(wú)法完成加載任務(wù),子加載器才會(huì)嘗試自己去加載,這就是雙親委派模型。

雙親委派模式的優(yōu)勢(shì):

避免重復(fù)加載;

考慮到安全因素,java核心api中定義類(lèi)型不會(huì)被隨意替換

假設(shè)通過(guò)網(wǎng)絡(luò)傳遞一個(gè)名為java.lang.Integer的類(lèi),通過(guò)雙親委派模式傳遞到啟動(dòng)加載器,而啟動(dòng)加載器在核心Java API中發(fā)現(xiàn)同名的類(lèi),發(fā)現(xiàn)該類(lèi)已經(jīng)被加載,就不會(huì)重新加載網(wǎng)絡(luò)傳遞的Integer類(lèi),而直接返回已加載過(guò)的Integer.class,這樣可以防止核心API庫(kù)被隨意篡改。

185、怎么判斷對(duì)象是否可以被回收?

1、引用計(jì)數(shù)算法

(1)判斷對(duì)象的引用數(shù)量

  • 通過(guò)判斷對(duì)象的引用數(shù)量來(lái)決定對(duì)象是否可以被回收;
  • 每個(gè)對(duì)象實(shí)例都有一個(gè)引用計(jì)數(shù)器,被引用+1,完成引用-1;
  • 任何引用計(jì)數(shù)為0的對(duì)象實(shí)例可以被當(dāng)做垃圾回收;

(2)優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):執(zhí)行效率高,程序受影響較??;
  • 缺點(diǎn):無(wú)法檢測(cè)出循環(huán)引用的情況,導(dǎo)致內(nèi)存泄漏;

2、可達(dá)性分析算法

通過(guò)判斷對(duì)象的引用鏈?zhǔn)欠窨蛇_(dá)來(lái)決定對(duì)象是否可以被回收。

如果程序無(wú)法再引用該對(duì)象,那么這個(gè)對(duì)象肯定可以被回收,這個(gè)狀態(tài)稱(chēng)為不可達(dá)。

那么不可達(dá)狀態(tài)如何判斷呢?

答案是GC roots,也就是根對(duì)象,如果一個(gè)對(duì)象無(wú)法到達(dá)根對(duì)象的路徑,或者說(shuō)從根對(duì)象無(wú)法引用到該對(duì)象,該對(duì)象就是不可達(dá)的。

以下三種對(duì)象在JVM中被稱(chēng)為GC roots,來(lái)判斷一個(gè)對(duì)象是否可以被回收。

(1)虛擬機(jī)棧的棧幀

每個(gè)方法在執(zhí)行的時(shí)候,JVM都會(huì)創(chuàng)建一個(gè)相應(yīng)的棧幀(操作數(shù)棧、局部變量表、運(yùn)行時(shí)常量池的引用),當(dāng)方法執(zhí)行完,該棧幀就從棧中彈出,這樣一來(lái),方法中臨時(shí)創(chuàng)建的獨(dú)享就不存在了,或者說(shuō)沒(méi)有任何GC roots指向這些臨時(shí)對(duì)象,這些對(duì)象在下一次GC的時(shí)候便會(huì)被回收。

(2)方法區(qū)中的靜態(tài)屬性

靜態(tài)屬性數(shù)據(jù)類(lèi)屬性,不屬于任何實(shí)例,因此該屬性自然會(huì)作為GC roots。這要這個(gè)class在,該引用指向的對(duì)象就一直存在,class也由被回收的時(shí)候。

class何時(shí)會(huì)被回收?

堆中不存在該類(lèi)的任何實(shí)例加載該類(lèi)的classLoader已經(jīng)被回收該類(lèi)的java.lang.class對(duì)象沒(méi)有在任何地方被引用,也就是說(shuō)無(wú)法通過(guò)反射訪問(wèn)該類(lèi)的信息

(3)本地方法棧引用的對(duì)象

186、說(shuō)一下 jvm 有哪些垃圾回收算法?

1、對(duì)象是否已死算法

引用計(jì)數(shù)器算法可達(dá)性分析算法

2、GC算法

(1)標(biāo)記清除算法

如果對(duì)象被標(biāo)記后進(jìn)行清除,會(huì)帶來(lái)一個(gè)新的問(wèn)題--內(nèi)存碎片化。

如果下次有比較大的對(duì)象實(shí)例需要在堆上分配較大的內(nèi)存空間時(shí),可能會(huì)出現(xiàn)無(wú)法找到足夠的連續(xù)內(nèi)存而不得不再次觸發(fā)垃圾回收。

(2)復(fù)制算法(Java堆中新生代的垃圾回收算法)

  • 先標(biāo)記待回收內(nèi)存和不用回收內(nèi)存;
  • 將不用回收的內(nèi)存復(fù)制到新的內(nèi)存區(qū)域;
  • 就的內(nèi)存區(qū)域就可以被全部回收了,而新的內(nèi)存區(qū)域也是連續(xù)的;
  • 缺點(diǎn)是損失部分系統(tǒng)內(nèi)存,因?yàn)轵v出部分內(nèi)存進(jìn)行復(fù)制。

(3)標(biāo)記壓縮算法(Java堆中老年代的垃圾回收算法)

對(duì)于新生代,大部分對(duì)象都不會(huì)存活,所以復(fù)制算法較高效,但對(duì)于老年代,大部分對(duì)象可能要繼續(xù)存活,如果此時(shí)使用復(fù)制算法,效率會(huì)降低。

標(biāo)記壓縮算法首先還是標(biāo)記,將不用回收的內(nèi)存對(duì)象壓縮到內(nèi)存一端,此時(shí)即可清除邊界處的內(nèi)存,這樣就能避免復(fù)制算法帶來(lái)的效率問(wèn)題,同時(shí)也能避免內(nèi)存碎片化的問(wèn)題。

老年代的垃圾回收算法稱(chēng)為“Major GC”。

187、說(shuō)一下 jvm 有哪些垃圾回收器?

說(shuō)一下 jvm 有哪些垃圾回收器?

188、JVM棧堆概念,何時(shí)銷(xiāo)毀對(duì)象

類(lèi)在程序運(yùn)行的時(shí)候就會(huì)被加載,方法是在執(zhí)行的時(shí)候才會(huì)被加載,如果沒(méi)有任何引用了,Java自動(dòng)垃圾回收,也可以用System.gc()開(kāi)啟回收器,但是回收器不一定會(huì)馬上回收。

  • 靜態(tài)變量在類(lèi)裝載的時(shí)候進(jìn)行創(chuàng)建,在整個(gè)程序結(jié)束時(shí)按序銷(xiāo)毀;
  • 實(shí)例變量在類(lèi)實(shí)例化對(duì)象時(shí)創(chuàng)建,在對(duì)象銷(xiāo)毀的時(shí)候銷(xiāo)毀;
  • 局部變量在局部范圍內(nèi)使用時(shí)創(chuàng)建,跳出局部范圍時(shí)銷(xiāo)毀

189、新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么區(qū)別?

新生代回收器:Serial、ParNew、Parallel Scavenge

老年代回收器:Serial Old、Parallel Old、CMS

新生代回收器一般采用的是復(fù)制算法,復(fù)制算法效率較高,但是浪費(fèi)內(nèi)存;

老生代回收器一般采用標(biāo)記清楚算法,比如最常用的CMS;

190、詳細(xì)介紹一下 CMS 垃圾回收器?

CMS 垃圾回收器是Concurrent Mark Sweep,是一種同步的標(biāo)記-清除,CMS分為四個(gè)階段:

  • 初始標(biāo)記,標(biāo)記一下GC Root能直接關(guān)聯(lián)到的對(duì)象,會(huì)觸發(fā)“Stop The World”;
  • 并發(fā)標(biāo)記,通過(guò)GC Roots Tracing判斷對(duì)象是否在使用中;
  • 重新標(biāo)記,標(biāo)記期間產(chǎn)生對(duì)象的再次判斷,執(zhí)行時(shí)間較短,會(huì)觸發(fā)“Stop The World”;
  • 并發(fā)清除,清除對(duì)象,可以和用戶(hù)線程并發(fā)進(jìn)行;

191、簡(jiǎn)述分代垃圾回收器是怎么工作的?

分代回收器分為新生代和老年代,新生代大概占1/3,老年代大概占2/3;

新生代包括Eden、From Survivor、To Survivor;

Eden區(qū)和兩個(gè)survivor區(qū)的 的空間比例 為8:1:1 ;

垃圾回收器的執(zhí)行流程:

  1. 把 Eden + From Survivor 存活的對(duì)象放入 To Survivor 區(qū);
  2. 清空 Eden + From Survivor 分區(qū),F(xiàn)rom Survivor 和 To Survivor 分區(qū)交換;每次交換后存活的對(duì)象年齡+1,到達(dá)15,升級(jí)為老年代,大對(duì)象會(huì)直接進(jìn)入老年代;
  3. 老年代中當(dāng)空間到達(dá)一定占比,會(huì)觸發(fā)全局回收,老年代一般采取標(biāo)記-清除算法;

192、Redis是什么?

Redis是一個(gè)key-value存儲(chǔ)系統(tǒng),它支持存儲(chǔ)的value類(lèi)型相對(duì)更多,包括string、list、set、zset(sorted set --有序集合)和hash。

這些數(shù)據(jù)結(jié)構(gòu)都支持push/pop、add/remove及取交集并集和差集及更豐富的操作,而且這些操作都是原子性的。

在此基礎(chǔ)上,Redis支持各種不同方式的排序。

為了保證效率,數(shù)據(jù)都是緩存在內(nèi)存中,Redis會(huì)周期性的把更新的數(shù)據(jù)寫(xiě)入磁盤(pán)或者把修改操作寫(xiě)入追加的記錄文件,并且在此基礎(chǔ)上實(shí)現(xiàn)了master-slave(主從)同步。

193、Redis都有哪些使用場(chǎng)景?

Redis是基于內(nèi)存的nosql數(shù)據(jù)庫(kù),可以通過(guò)新建線程的形式進(jìn)行持久化,不影響Redis單線程的讀寫(xiě)操作通過(guò)list取最新的N條數(shù)據(jù)模擬類(lèi)似于token這種需要設(shè)置過(guò)期時(shí)間的場(chǎng)景發(fā)布訂閱消息系統(tǒng)定時(shí)器、計(jì)數(shù)器

194、Redis有哪些功能?

1、基于本機(jī)內(nèi)存的緩存

當(dāng)調(diào)用api訪問(wèn)數(shù)據(jù)庫(kù)時(shí),假如此過(guò)程需要2秒,如果每次請(qǐng)求都要訪問(wèn)數(shù)據(jù)庫(kù),那將對(duì)服務(wù)器造成巨大的壓力,如果將此sql的查詢(xún)結(jié)果存到Redis中,再次請(qǐng)求時(shí),直接從Redis中取得,而不是訪問(wèn)數(shù)據(jù)庫(kù),效率將得到巨大的提升,Redis可以定時(shí)去更新數(shù)據(jù)(比如1分鐘)。

2、如果電腦重啟,寫(xiě)入內(nèi)存的數(shù)據(jù)是不是就失效了呢,這時(shí)Redis還提供了持久化的功能。

3、哨兵(Sentinel)和復(fù)制

Sentinel可以管理多個(gè)Redis服務(wù)器,它提供了監(jiān)控、提醒以及自動(dòng)的故障轉(zhuǎn)移功能;

復(fù)制則是讓Redis服務(wù)器可以配備備份的服務(wù)器;

Redis也是通過(guò)這兩個(gè)功能保證Redis的高可用;

4、集群(Cluster)

單臺(tái)服務(wù)器資源總是有上限的,CPU和IO資源可以通過(guò)主從復(fù)制,進(jìn)行讀寫(xiě)分離,把一部分CPU和IO的壓力轉(zhuǎn)移到從服務(wù)器上,但是內(nèi)存資源怎么辦,主從模式只是數(shù)據(jù)的備份,并不能擴(kuò)充內(nèi)存;

現(xiàn)在我們可以橫向擴(kuò)展,讓每臺(tái)服務(wù)器只負(fù)責(zé)一部分任務(wù),然后將這些服務(wù)器構(gòu)成一個(gè)整體,對(duì)外界來(lái)說(shuō),這一組服務(wù)器就像是集群一樣。

195、Redis支持的數(shù)據(jù)類(lèi)型有哪些?

字符串hashlistsetzset

196、Redis取值存值問(wèn)題

1、先把Redis的連接池拿出來(lái)

JedisPool pool = new JedisPool(new JedisPoolConfig(),"127.0.0.1");
 
Jedis jedis = pool.getResource();

2、存取值

jedis.set("key","value");
jedis.get("key");
jedis.del("key");
//給一個(gè)key疊加value
jedis.append("key","value2");//此時(shí)key的值就是value + value2;
//同時(shí)給多個(gè)key進(jìn)行賦值:
jedis.mset("key1","value1","key2","value2");

3、對(duì)map進(jìn)行操作

Map<String,String> user = new HashMap();
user.put("key1","value1");
user.put("key2","value2");
user.put("key3","value3");
//存入
jedis.hmset("user",user);
//取出user中key1 
List<String> nameMap = jedis.hmget("user","key1");
//刪除其中一個(gè)鍵值
jedis.hdel("user","key2");
//是否存在一個(gè)鍵
jedis.exists("user");
//取出所有的Map中的值:
Iterator<String> iter = jedis.hkeys("user").iterator();
while(iter.next()){
    jedis.hmget("user",iter.next());
}

197、Redis為什么是單線程的?

  • 代碼更清晰,處理邏輯更簡(jiǎn)單;
  • 不用考慮各種鎖的問(wèn)題,不存在加鎖和釋放鎖的操作,沒(méi)有因?yàn)榭赡艹霈F(xiàn)死鎖而導(dǎo)致的性能問(wèn)題;
  • 不存在多線程切換而消耗CPU;
  • 無(wú)法發(fā)揮多核CPU的優(yōu)勢(shì),但可以采用多開(kāi)幾個(gè)Redis實(shí)例來(lái)完善

198、Redis真的是單線程的嗎?

Redis6.0之前是單線程的,Redis6.0之后開(kāi)始支持多線程;

redis內(nèi)部使用了基于epoll的多路服用,也可以多部署幾個(gè)redis服務(wù)器解決單線程的問(wèn)題;

redis主要的性能瓶頸是內(nèi)存和網(wǎng)絡(luò);

內(nèi)存好說(shuō),加內(nèi)存條就行了,而網(wǎng)絡(luò)才是大 麻煩,所以redis6內(nèi)存好說(shuō),加內(nèi)存條就行了;

而網(wǎng)絡(luò)才是大 麻煩,所以redis6.0引入了多線程的概念,

redis6.0在網(wǎng)絡(luò)IO處理方面引入了多線程,如網(wǎng)絡(luò)數(shù)據(jù)的讀寫(xiě)和協(xié)議解析等,需要注意的是,執(zhí)行命令的核心模塊還是單線程的。

199、Redis持久化有幾種方式?

redis提供了兩種持久化的方式,分別是RDB(Redis DataBase)和AOF(Append Only File)。

RDB,簡(jiǎn)而言之,就是在不同的時(shí)間點(diǎn),將redis存儲(chǔ)的數(shù)據(jù)生成快照并存儲(chǔ)到磁盤(pán)等介質(zhì)上;

AOF,則是換了一個(gè)角度來(lái)實(shí)現(xiàn)持久化,那就是將redis執(zhí)行過(guò)的所有寫(xiě)指令記錄下來(lái),在下次redis重新啟動(dòng)時(shí),只要把這些寫(xiě)指令從前到后再重復(fù)執(zhí)行一遍,就可以實(shí)現(xiàn)數(shù)據(jù)恢復(fù)了。

其實(shí)RDB和AOF兩種方式也可以同時(shí)使用,在這種情況下,如果redis重啟的話,則會(huì)優(yōu)先采用AOF方式來(lái)進(jìn)行數(shù)據(jù)恢復(fù),這是因?yàn)锳OF方式的數(shù)據(jù)恢復(fù)完整度更高。

如果你沒(méi)有數(shù)據(jù)持久化的需求,也完全可以關(guān)閉RDB和AOF方式,這樣的話,redis將變成一個(gè)純內(nèi)存數(shù)據(jù)庫(kù),就像memcache一樣。

200、Redis和 memecache 有什么區(qū)別?

1、Redis相比memecache,擁有更多的數(shù)據(jù)結(jié)構(gòu)和支持更豐富的數(shù)據(jù)操作。

(1)Redis支持key-value,常用的數(shù)據(jù)類(lèi)型主要有String、Hash、List、Set、Sorted Set。

(2)memecache只支持key-value。

2、內(nèi)存使用率對(duì)比,Redis采用hash結(jié)構(gòu)來(lái)做key-value存儲(chǔ),由于其組合式的壓縮,其內(nèi)存利用率會(huì)高于memecache。

3、性能對(duì)比:Redis只使用單核,memecache使用多核。

4、Redis支持磁盤(pán)持久化,memecache不支持。

Redis可以將一些很久沒(méi)用到的value通過(guò)swap方法交換到磁盤(pán)。

5、Redis支持分布式集群,memecache不支持。

201、Redis支持的 java 客戶(hù)端都有哪些?

Redisson、Jedis、lettuce 等等,官方推薦使用 Redisson。

202、jedis 和 redisson 有哪些區(qū)別?

Jedis 和 Redisson 都是Java中對(duì)Redis操作的封裝。

Jedis 只是簡(jiǎn)單的封裝了 Redis 的API庫(kù),可以看作是Redis客戶(hù)端,它的方法和Redis 的命令很類(lèi)似。

Redisson 不僅封裝了 redis ,還封裝了對(duì)更多數(shù)據(jù)結(jié)構(gòu)的支持,以及鎖等功能,相比于Jedis 更加大。

但Jedis相比于Redisson 更原生一些,更靈活。

203、什么是緩存穿透?怎么解決?

1、緩存穿透

一般的緩存系統(tǒng),都是按照key去緩存查詢(xún),如果不存在對(duì)用的value,就應(yīng)該去后端系統(tǒng)查找(比如DB數(shù)據(jù)庫(kù))。

一些惡意的請(qǐng)求會(huì)故意查詢(xún)不存在的key,請(qǐng)求量很大,就會(huì)對(duì)后端系統(tǒng)造成很大的壓力。

這就叫做緩存穿透。

2、怎么解決?

對(duì)查詢(xún)結(jié)果為空的情況也進(jìn)行緩存,緩存時(shí)間設(shè)置短一點(diǎn),或者該key對(duì)應(yīng)的數(shù)據(jù)insert之后清理緩存。

對(duì)一定不存在的key進(jìn)行過(guò)濾。可以把所有的可能存在的key放到一個(gè)大的Bitmap中,查詢(xún)時(shí)通過(guò)該Bitmap過(guò)濾。

3、緩存雪崩

當(dāng)緩存服務(wù)器重啟或者大量緩存集中在某一時(shí)間段失效,這樣在失效的時(shí)候,會(huì)給后端系統(tǒng)帶來(lái)很大的壓力,導(dǎo)致系統(tǒng)崩潰。

4、如何解決?

在緩存失效后,通過(guò)加鎖或者隊(duì)列來(lái)控制讀數(shù)據(jù)庫(kù)寫(xiě)緩存的線程數(shù)量。

比如

  • 對(duì)某個(gè)key只允許一個(gè)線程查詢(xún)數(shù)據(jù)和寫(xiě)緩存,其它線程等待;
  • 做二級(jí)緩存;
  • 不同的key,設(shè)置不同的過(guò)期時(shí)間,讓緩存失效的時(shí)間盡量均勻;

204、怎么保證緩存和數(shù)據(jù)庫(kù)數(shù)據(jù)的一致性?

1、淘汰緩存

數(shù)據(jù)如果為較為復(fù)雜的數(shù)據(jù)時(shí),進(jìn)行緩存的更新操作就會(huì)變得異常復(fù)雜,因此一般推薦選擇淘汰緩存,而不是更新緩存。

2、選擇先淘汰緩存,再更新數(shù)據(jù)庫(kù)

假如先更新數(shù)據(jù)庫(kù),再淘汰緩存,如果淘汰緩存失敗,那么后面的請(qǐng)求都會(huì)得到臟數(shù)據(jù),直至緩存過(guò)期。

假如先淘汰緩存再更新數(shù)據(jù)庫(kù),如果更新數(shù)據(jù)庫(kù)失敗,只會(huì)產(chǎn)生一次緩存穿透,相比較而言,后者對(duì)業(yè)務(wù)則沒(méi)有本質(zhì)上的影響。

3、延時(shí)雙刪策略

如下場(chǎng)景:同時(shí)有一個(gè)請(qǐng)求A進(jìn)行更新操作,另一個(gè)請(qǐng)求B進(jìn)行查詢(xún)操作。

請(qǐng)求A進(jìn)行寫(xiě)操作,刪除緩存請(qǐng)求B查詢(xún)發(fā)現(xiàn)緩存不存在請(qǐng)求B去數(shù)據(jù)庫(kù)查詢(xún)得到舊值請(qǐng)求B將舊值寫(xiě)入緩存請(qǐng)求A將新值寫(xiě)入數(shù)據(jù)庫(kù)

次數(shù)便出現(xiàn)了數(shù)據(jù)不一致問(wèn)題。采用延時(shí)雙刪策略得以解決。

public void write(String key,Object data){
    redisUtils.del(key);
    db.update(data);
    Thread.Sleep(100);
    redisUtils.del(key);
}

這么做,可以將1秒內(nèi)所造成的緩存臟數(shù)據(jù),再次刪除。這個(gè)時(shí)間設(shè)定可根據(jù)俄業(yè)務(wù)場(chǎng)景進(jìn)行一個(gè)調(diào)節(jié)。

4、數(shù)據(jù)庫(kù)讀寫(xiě)分離的場(chǎng)景

兩個(gè)請(qǐng)求,一個(gè)請(qǐng)求A進(jìn)行更新操作,另一個(gè)請(qǐng)求B進(jìn)行查詢(xún)操作。

  1. 請(qǐng)求A進(jìn)行寫(xiě)操作,刪除緩存
  2. 請(qǐng)求A將數(shù)據(jù)寫(xiě)入數(shù)據(jù)庫(kù)了
  3. 請(qǐng)求B查詢(xún)緩存發(fā)現(xiàn),緩存沒(méi)有值
  4. 請(qǐng)求B去從庫(kù)查詢(xún)這時(shí),還沒(méi)有完成主從同步,因此查詢(xún)到的是舊值
  5. 請(qǐng)求B將舊值寫(xiě)入緩存
  6. 數(shù)據(jù)庫(kù)完成主從同步,從庫(kù)變?yōu)樾轮?/li>

依舊采用延時(shí)雙刪策略解決此問(wèn)題。

205、Redis,什么是緩存穿透?怎么解決?

1、緩存穿透

一般的緩存系統(tǒng),都是按照key去緩存查詢(xún),如果不存在對(duì)用的value,就應(yīng)該去后端系統(tǒng)查找(比如DB數(shù)據(jù)庫(kù))。

一些惡意的請(qǐng)求會(huì)故意查詢(xún)不存在的key,請(qǐng)求量很大,就會(huì)對(duì)后端系統(tǒng)造成很大的壓力。

這就叫做緩存穿透。

2、怎么解決?

對(duì)查詢(xún)結(jié)果為空的情況也進(jìn)行緩存,緩存時(shí)間設(shè)置短一點(diǎn),或者該key對(duì)應(yīng)的數(shù)據(jù)insert之后清理緩存。

對(duì)一定不存在的key進(jìn)行過(guò)濾??梢园阉械目赡艽嬖诘膋ey放到一個(gè)大的Bitmap中,查詢(xún)時(shí)通過(guò)該Bitmap過(guò)濾。

3、緩存雪崩

當(dāng)緩存服務(wù)器重啟或者大量緩存集中在某一時(shí)間段失效,這樣在失效的時(shí)候,會(huì)給后端系統(tǒng)帶來(lái)很大的壓力,導(dǎo)致系統(tǒng)崩潰。

4、如何解決?

在緩存失效后,通過(guò)加鎖或者隊(duì)列來(lái)控制讀數(shù)據(jù)庫(kù)寫(xiě)緩存的線程數(shù)量。

比如

  1. 對(duì)某個(gè)key只允許一個(gè)線程查詢(xún)數(shù)據(jù)和寫(xiě)緩存,其它線程等待;
  2. 做二級(jí)緩存;
  3. 不同的key,設(shè)置不同的過(guò)期時(shí)間,讓緩存失效的時(shí)間盡量均勻;

206、Redis怎么實(shí)現(xiàn)分布式鎖?

使用Redis實(shí)現(xiàn)分布式鎖

redis命令:set users 10 nx ex 12   原子性命令

//使用uuid,解決鎖釋放的問(wèn)題
@GetMapping
public void testLock() throws InterruptedException {
    String uuid = UUID.randomUUID().toString();
    Boolean b_lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 10, TimeUnit.SECONDS);
    if(b_lock){
        Object value = redisTemplate.opsForValue().get("num");
        if(StringUtils.isEmpty(value)){
            return;
        }
        int num = Integer.parseInt(value + "");
        redisTemplate.opsForValue().set("num",++num);
        Object lockUUID = redisTemplate.opsForValue().get("lock");
        if(uuid.equals(lockUUID.toString())){
            redisTemplate.delete("lock");
        }
    }else{
        Thread.sleep(100);
        testLock();
    }
}

備注:可以通過(guò)lua腳本,保證分布式鎖的原子性。

207、Redis分布式鎖有什么缺陷?

Redis 分布式鎖不能解決超時(shí)的問(wèn)題,分布式鎖有一個(gè)超時(shí)時(shí)間,程序的執(zhí)行如果超出了鎖的超時(shí)時(shí)間就會(huì)出現(xiàn)問(wèn)題。

Redis容易產(chǎn)生的幾個(gè)問(wèn)題:

  1. 鎖未被釋放
  2. B鎖被A鎖釋放了
  3. 數(shù)據(jù)庫(kù)事務(wù)超時(shí)
  4. 鎖過(guò)期了,業(yè)務(wù)還沒(méi)執(zhí)行完
  5. Redis主從復(fù)制的問(wèn)題

208、Redis如何做內(nèi)存優(yōu)化?

1、縮短鍵值的長(zhǎng)度

  1. 縮短值的長(zhǎng)度才是關(guān)鍵,如果值是一個(gè)大的業(yè)務(wù)對(duì)象,可以將對(duì)象序列化成二進(jìn)制數(shù)組;
  2. 首先應(yīng)該在業(yè)務(wù)上進(jìn)行精簡(jiǎn),去掉不必要的屬性,避免存儲(chǔ)一些沒(méi)用的數(shù)據(jù);
  3. 其次是序列化的工具選擇上,應(yīng)該選擇更高效的序列化工具來(lái)降低字節(jié)數(shù)組大??;
  4. 以Java為例,內(nèi)置的序列化方式無(wú)論從速度還是壓縮比都不盡如人意,這時(shí)可以選擇更高效的序列化工具,如: protostuff,kryo等

2、共享對(duì)象池

對(duì)象共享池指Redis內(nèi)部維護(hù)[0-9999]的整數(shù)對(duì)象池

。創(chuàng)建大量的整數(shù)類(lèi)型redisObject存在內(nèi)存開(kāi)銷(xiāo),每個(gè)redisObject內(nèi)部結(jié)構(gòu)至少占16字節(jié),甚至超過(guò)了整數(shù)自身空間消耗。

所以Redis內(nèi)存維護(hù)一個(gè)[0-9999]的整數(shù)對(duì)象池,用于節(jié)約內(nèi)存。

除了整數(shù)值對(duì)象,其他類(lèi)型如list,hash,set,zset內(nèi)部元素也可以使用整數(shù)對(duì)象池。

因此開(kāi)發(fā)中在滿(mǎn)足需求的前提下,盡量使用整數(shù)對(duì)象以節(jié)省內(nèi)存。

3、字符串優(yōu)化

4、編碼優(yōu)化

5、控制key的數(shù)量

到此這篇關(guān)于Java經(jīng)典面試題最全匯總208道(六)的文章就介紹到這了,更多相關(guān)Java面試題內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • dm.jdbc.driver.DMException網(wǎng)絡(luò)通信異常的解決過(guò)程

    dm.jdbc.driver.DMException網(wǎng)絡(luò)通信異常的解決過(guò)程

    最近一個(gè)項(xiàng)目里面出現(xiàn)了一個(gè)比較詭異的問(wèn)題,給大家分享下,這篇文章主要給大家介紹了關(guān)于dm.jdbc.driver.DMException網(wǎng)絡(luò)通信異常的解決過(guò)程,需要的朋友可以參考下
    2023-02-02
  • Java中的FutureTask實(shí)現(xiàn)異步任務(wù)代碼實(shí)例

    Java中的FutureTask實(shí)現(xiàn)異步任務(wù)代碼實(shí)例

    這篇文章主要介紹了Java中的FutureTask實(shí)現(xiàn)異步任務(wù)代碼實(shí)例,普通的線程執(zhí)行是無(wú)法獲取到執(zhí)行結(jié)果的,FutureTask?間接實(shí)現(xiàn)了?Runnable?和?Future?接口,可以得到子線程耗時(shí)操作的執(zhí)行結(jié)果,AsyncTask?異步任務(wù)就是使用了該機(jī)制,需要的朋友可以參考下
    2024-01-01
  • java web項(xiàng)目實(shí)現(xiàn)文件下載實(shí)例代碼

    java web項(xiàng)目實(shí)現(xiàn)文件下載實(shí)例代碼

    現(xiàn)在項(xiàng)目里面有個(gè)需求,需要把系統(tǒng)產(chǎn)生的日志文件給下載到本地 先獲取所有的日志文件列表,顯示到界面,選擇一個(gè)日志文件,把文件名傳到后臺(tái)
    2013-09-09
  • SpringBoot中使用Ehcache的詳細(xì)教程

    SpringBoot中使用Ehcache的詳細(xì)教程

    EhCache 是一個(gè)純 Java 的進(jìn)程內(nèi)緩存框架,具有快速、精干等特點(diǎn),是 Hibernate 中默認(rèn)的 CacheProvider。這篇文章主要介紹了SpringBoot中使用Ehcache的相關(guān)知識(shí),需要的朋友可以參考下
    2020-08-08
  • Java中redis的基本類(lèi)型

    Java中redis的基本類(lèi)型

    這篇文章主要介紹了Java中redis的基本類(lèi)型,redis存儲(chǔ)數(shù)據(jù)的基本類(lèi)型有:字符串類(lèi)型、散列類(lèi)型、列表類(lèi)型、集合類(lèi)型、有序集合類(lèi)型,下面我們對(duì)其中幾個(gè)進(jìn)行簡(jiǎn)單介紹,需要的小伙伴可以參考一下
    2022-03-03
  • IntelliJ IDEA引入第三方j(luò)ar包或查看Java源碼的時(shí)候報(bào)decompiled.class file bytecode version:52.0(java 8)錯(cuò)誤的解決辦法

    IntelliJ IDEA引入第三方j(luò)ar包或查看Java源碼的時(shí)候報(bào)decompiled.class file byt

    今天小編就為大家分享一篇關(guān)于IntelliJ IDEA引入第三方j(luò)ar包或查看Java源碼的時(shí)候報(bào)decompiled.class file bytecode version:52.0(java 8)錯(cuò)誤的解決辦法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-10-10
  • SpringBoot使用Sharding-JDBC實(shí)現(xiàn)數(shù)據(jù)分片和讀寫(xiě)分離的方法

    SpringBoot使用Sharding-JDBC實(shí)現(xiàn)數(shù)據(jù)分片和讀寫(xiě)分離的方法

    本文主要介紹了SpringBoot使用Sharding-JDBC實(shí)現(xiàn)數(shù)據(jù)分片和讀寫(xiě)分離,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • Spring Boot和Vue跨域請(qǐng)求問(wèn)題原理解析

    Spring Boot和Vue跨域請(qǐng)求問(wèn)題原理解析

    這篇文章主要介紹了Spring Boot和Vue跨域請(qǐng)求問(wèn)題原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • 圖文詳解JAVA實(shí)現(xiàn)快速排序

    圖文詳解JAVA實(shí)現(xiàn)快速排序

    這篇文章主要給大家介紹了關(guān)于JAVA實(shí)現(xiàn)快速排序的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • SpringBoot中分頁(yè)插件PageHelper的使用詳解

    SpringBoot中分頁(yè)插件PageHelper的使用詳解

    分頁(yè)查詢(xún)是為了高效展示大量數(shù)據(jù),通過(guò)分頁(yè)將數(shù)據(jù)劃分為多個(gè)部分逐頁(yè)展示,原生方法需手動(dòng)計(jì)算數(shù)據(jù)起始行,而使用PageHelper插件則簡(jiǎn)化這一過(guò)程,本文給大家介紹SpringBoot中分頁(yè)插件PageHelper的使用,感興趣的朋友一起看看吧
    2024-09-09

最新評(píng)論