小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

敖丙在位置上肝了一個(gè)月的后端知識(shí)點(diǎn)長(zhǎng)啥樣?

 鷹兔牛熊眼 2020-05-08

前言

前段時(shí)間敖丙不是在復(fù)習(xí)嘛,很多小伙伴也想要我的復(fù)習(xí)路線,以及我自己筆記里面的一些知識(shí)點(diǎn),好了,丙丙花了一個(gè)月的時(shí)間,整整一個(gè)月啊,給大家整理出來(lái)了。

一上來(lái)我就放個(gè)大招好吧,我的復(fù)習(xí)腦圖,可以說(shuō)是全得不行,為了防止被盜圖,我加了水印哈。

這期看下去你會(huì)發(fā)現(xiàn)很硬核,而且我會(huì)持續(xù)更新,啥也不說(shuō)了,看在我熬夜一個(gè)月滿臉痘痘的份上,你可以點(diǎn)贊了哈哈。

注:如果圖被壓縮了,可以去公眾號(hào)【三太子敖丙】回復(fù)【復(fù)習(xí)】獲取原圖

Spring

Spring框架的七大模塊

Spring Core:框架的最基礎(chǔ)部分,提供 IoC 容器,對(duì) bean 進(jìn)行管理。

Spring Context:繼承BeanFactory,提供上下文信息,擴(kuò)展出JNDI、EJB、電子郵件、國(guó)際化等功能。

Spring DAO:提供了JDBC的抽象層,還提供了聲明性事務(wù)管理方法。

Spring ORM:提供了JPA、JDO、Hibernate、MyBatis 等ORM映射層.

Spring AOP:集成了所有AOP功能

Spring Web:提供了基礎(chǔ)的 Web 開(kāi)發(fā)的上下文信息,現(xiàn)有的Web框架,如JSF、Tapestry、Structs等,提供了集成

Spring Web MVC:提供了 Web 應(yīng)用的 Model-View-Controller 全功能實(shí)現(xiàn)。

Bean定義5種作用域

singleton(單例)prototype(原型)requestsessionglobal session

spring ioc初始化流程?

resource定位即尋找用戶定義的bean資源,由 ResourceLoader通過(guò)統(tǒng)一的接口Resource接口來(lái)完成beanDefinition載入BeanDefinitionReader讀取、解析Resource定位的資源 成BeanDefinition 載入到ioc中(通過(guò)HashMap進(jìn)行維護(hù)BD)BeanDefinition注冊(cè)即向IOC容器注冊(cè)這些BeanDefinition, 通過(guò)BeanDefinitionRegistery實(shí)現(xiàn)

BeanDefinition加載流程?

定義BeanDefinitionReader解析xml的documentBeanDefinitionDocumentReader解析document成beanDefinition

DI依賴(lài)注入流程? (實(shí)例化,處理Bean之間的依賴(lài)關(guān)系)

過(guò)程在Ioc初始化后,依賴(lài)注入的過(guò)程是用戶第一次向IoC容器索要Bean時(shí)觸發(fā)

  • 如果設(shè)置lazy-init=true,會(huì)在第一次getBean的時(shí)候才初始化bean, lazy-init=false,會(huì)容器啟動(dòng)的時(shí)候直接初始化(singleton bean);

  • 調(diào)用BeanFactory.getBean()生成bean的;

  • 生成bean過(guò)程運(yùn)用裝飾器模式產(chǎn)生的bean都是beanWrapper(bean的增強(qiáng));

    依賴(lài)注入怎么處理bean之間的依賴(lài)關(guān)系?

    其實(shí)就是通過(guò)在beanDefinition載入時(shí),如果bean有依賴(lài)關(guān)系,通過(guò)占位符來(lái)代替,在調(diào)用getbean時(shí)候,如果遇到占位符,從ioc里獲取bean注入到本實(shí)例來(lái)

Bean的生命周期?

  • 實(shí)例化Bean:Ioc容器通過(guò)獲取BeanDefinition對(duì)象中的信息進(jìn)行實(shí)例化,實(shí)例化對(duì)象被包裝在BeanWrapper對(duì)象中
  • 設(shè)置對(duì)象屬性(DI):通過(guò)BeanWrapper提供的設(shè)置屬性的接口完成屬性依賴(lài)注入;
  • 注入Aware接口(BeanFactoryAware, 可以用這個(gè)方式來(lái)獲取其它 Bean,ApplicationContextAware):Spring會(huì)檢測(cè)該對(duì)象是否實(shí)現(xiàn)了xxxAware接口,并將相關(guān)的xxxAware實(shí)例注入給bean
  • BeanPostProcessor:自定義的處理(分前置處理和后置處理)
  • InitializingBean和init-method:執(zhí)行我們自己定義的初始化方法
  • 使用
  • destroy:bean的銷(xiāo)毀

IOC:控制反轉(zhuǎn):將對(duì)象的創(chuàng)建權(quán),由Spring管理.DI(依賴(lài)注入):在Spring創(chuàng)建對(duì)象的過(guò)程中,把對(duì)象依賴(lài)的屬性注入到類(lèi)中。

Spring的IOC注入方式

構(gòu)造器注入setter方法注入注解注入接口注入

怎么檢測(cè)是否存在循環(huán)依賴(lài)?

Bean在創(chuàng)建的時(shí)候可以給該Bean打標(biāo),如果遞歸調(diào)用回來(lái)發(fā)現(xiàn)正在創(chuàng)建中的話,即說(shuō)明了循環(huán)依賴(lài)了。

Spring如解決Bean循環(huán)依賴(lài)問(wèn)題?

Spring中循環(huán)依賴(lài)場(chǎng)景有:

  • 構(gòu)造器的循環(huán)依賴(lài)
  • 屬性的循環(huán)依賴(lài)
  • singletonObjects:第一級(jí)緩存,里面放置的是實(shí)例化好的單例對(duì)象;earlySingletonObjects:第二級(jí)緩存,里面存放的是提前曝光的單例對(duì)象;singletonFactories:第三級(jí)緩存,里面存放的是要被實(shí)例化的對(duì)象的對(duì)象工廠
  • 創(chuàng)建bean的時(shí)候Spring首先從一級(jí)緩存singletonObjects中獲取。如果獲取不到,并且對(duì)象正在創(chuàng)建中,就再?gòu)亩?jí)緩存earlySingletonObjects中獲取,如果還是獲取不到就從三級(jí)緩存singletonFactories中?。˙ean調(diào)用構(gòu)造函數(shù)進(jìn)行實(shí)例化后,即使屬性還未填充,就可以通過(guò)三級(jí)緩存向外提前暴露依賴(lài)的引用值(提前曝光),根據(jù)對(duì)象引用能定位到堆中的對(duì)象,其原理是基于Java的引用傳遞),取到后從三級(jí)緩存移動(dòng)到了二級(jí)緩存完全初始化之后將自己放入到一級(jí)緩存中供其他使用,
  • 因?yàn)榧尤雜ingletonFactories三級(jí)緩存的前提是執(zhí)行了構(gòu)造器,所以構(gòu)造器的循環(huán)依賴(lài)沒(méi)法解決。
  • 構(gòu)造器循環(huán)依賴(lài)解決辦法:在構(gòu)造函數(shù)中使用@Lazy注解延遲加載。在注入依賴(lài)時(shí),先注入代理對(duì)象,當(dāng)首次使用時(shí)再創(chuàng)建對(duì)象說(shuō)明:一種互斥的關(guān)系而非層次遞進(jìn)的關(guān)系,故稱(chēng)為三個(gè)Map而非三級(jí)緩存的緣由完成注入;

Spring 中使用了哪些設(shè)計(jì)模式?

  • 工廠模式:spring中的BeanFactory就是簡(jiǎn)單工廠模式的體現(xiàn),根據(jù)傳入唯一的標(biāo)識(shí)來(lái)獲得bean對(duì)象;
  • 單例模式:提供了全局的訪問(wèn)點(diǎn)BeanFactory;
  • 代理模式:AOP功能的原理就使用代理模式(1、JDK動(dòng)態(tài)代理。2、CGLib字節(jié)碼生成技術(shù)代理。)
  • 裝飾器模式:依賴(lài)注入就需要使用BeanWrapper;
  • 觀察者模式:spring中Observer模式常用的地方是listener的實(shí)現(xiàn)。如ApplicationListener。
  • 策略模式:Bean的實(shí)例化的時(shí)候決定采用何種方式初始化bean實(shí)例(反射或者CGLIB動(dòng)態(tài)字節(jié)碼生成)

AOP 核心概念

1、切面(aspect):類(lèi)是對(duì)物體特征的抽象,切面就是對(duì)橫切關(guān)注點(diǎn)的抽象

2、橫切關(guān)注點(diǎn):對(duì)哪些方法進(jìn)行攔截,攔截后怎么處理,這些關(guān)注點(diǎn)稱(chēng)之為橫切關(guān)注點(diǎn)。

3、連接點(diǎn)(joinpoint):被攔截到的點(diǎn),因?yàn)?Spring 只支持方法類(lèi)型的連接點(diǎn),所以在Spring 中連接點(diǎn)指的就是被攔截到的方法,實(shí)際上連接點(diǎn)還可以是字段或者構(gòu)造器。

4、切入點(diǎn)(pointcut):對(duì)連接點(diǎn)進(jìn)行攔截的定義

5、通知(advice):所謂通知指的就是指攔截到連接點(diǎn)之后要執(zhí)行的代碼,通知分為前置、后置、異常、最終、環(huán)繞通知五類(lèi)。

6、目標(biāo)對(duì)象:代理的目標(biāo)對(duì)象

7、織入(weave):將切面應(yīng)用到目標(biāo)對(duì)象并導(dǎo)致代理對(duì)象創(chuàng)建的過(guò)程

8、引入(introduction):在不修改代碼的前提下,引入可以在運(yùn)行期為類(lèi)動(dòng)態(tài)地添加方法或字段。

解釋一下AOP

傳統(tǒng)oop開(kāi)發(fā)代碼邏輯自上而下的,這個(gè)過(guò)程中會(huì)產(chǎn)生一些橫切性問(wèn)題,這些問(wèn)題與我們主業(yè)務(wù)邏輯關(guān)系不大,會(huì)散落在代碼的各個(gè)地方,造成難以維護(hù),aop思想就是把業(yè)務(wù)邏輯與橫切的問(wèn)題進(jìn)行分離,達(dá)到解耦的目的,提高代碼重用性和開(kāi)發(fā)效率;

AOP 主要應(yīng)用場(chǎng)景有:

  • 記錄日志
  • 監(jiān)控性能
  • 權(quán)限控制
  • 事務(wù)管理

AOP源碼分析

  • @EnableAspectJAutoProxy給容器(beanFactory)中注冊(cè)一個(gè)AnnotationAwareAspectJAutoProxyCreator對(duì)象;

  • AnnotationAwareAspectJAutoProxyCreator對(duì)目標(biāo)對(duì)象進(jìn)行代理對(duì)象的創(chuàng)建,對(duì)象內(nèi)部,是封裝JDK和CGlib兩個(gè)技術(shù),實(shí)現(xiàn)動(dòng)態(tài)代理對(duì)象創(chuàng)建的(創(chuàng)建代理對(duì)象過(guò)程中,會(huì)先創(chuàng)建一個(gè)代理工廠,獲取到所有的增強(qiáng)器(通知方法),將這些增強(qiáng)器和目標(biāo)類(lèi)注入代理工廠,再用代理工廠創(chuàng)建對(duì)象);

  • 代理對(duì)象執(zhí)行目標(biāo)方法,得到目標(biāo)方法的攔截器鏈,利用攔截器的鏈?zhǔn)綑C(jī)制,依次進(jìn)入每一個(gè)攔截器進(jìn)行執(zhí)行

    AOP應(yīng)用場(chǎng)景

    • 日志記錄
    • 事務(wù)管理
    • 線程池關(guān)閉等

AOP使用哪種動(dòng)態(tài)代理?

  • 當(dāng)bean的是實(shí)現(xiàn)中存在接口或者是Proxy的子類(lèi),---jdk動(dòng)態(tài)代理;不存在接口,spring會(huì)采用CGLIB來(lái)生成代理對(duì)象;
  • JDK 動(dòng)態(tài)代理主要涉及到 java.lang.reflect 包中的兩個(gè)類(lèi):Proxy 和 InvocationHandler。
  • Proxy 利用 InvocationHandler(定義橫切邏輯) 接口動(dòng)態(tài)創(chuàng)建 目標(biāo)類(lèi)的代理對(duì)象。

jdk動(dòng)態(tài)代理

  • 通過(guò)bind方法建立代理與真實(shí)對(duì)象關(guān)系,通過(guò)Proxy.newProxyInstance(target)生成代理對(duì)象
  • 代理對(duì)象通過(guò)反射invoke方法實(shí)現(xiàn)調(diào)用真實(shí)對(duì)象的方法

動(dòng)態(tài)代理與靜態(tài)代理區(qū)別

  • 靜態(tài)代理,程序運(yùn)行前代理類(lèi)的.class文件就存在了;
  • 動(dòng)態(tài)代理:在程序運(yùn)行時(shí)利用反射動(dòng)態(tài)創(chuàng)建代理對(duì)象<復(fù)用性,易用性,更加集中都調(diào)用invoke>

CGLIB與JDK動(dòng)態(tài)代理區(qū)別

  • Jdk必須提供接口才能使用;
  • C不需要,只要一個(gè)非抽象類(lèi)就能實(shí)現(xiàn)動(dòng)態(tài)代理

SpringMVC

springMVC流程:

(1):用戶請(qǐng)求發(fā)送給DispatcherServlet,DispatcherServlet調(diào)用HandlerMapping處理器映射器;

(2):HandlerMapping根據(jù)xml或注解找到對(duì)應(yīng)的處理器,生成處理器對(duì)象返回給DispatcherServlet;

(3):DispatcherServlet會(huì)調(diào)用相應(yīng)的HandlerAdapter;

(4):HandlerAdapter經(jīng)過(guò)適配調(diào)用具體的處理器去處理請(qǐng)求,生成ModelAndView返回給DispatcherServlet

(5):DispatcherServlet將ModelAndView傳給ViewReslover解析生成View返回給DispatcherServlet;

(6):DispatcherServlet根據(jù)View進(jìn)行渲染視圖;

->DispatcherServlet->HandlerMapping->Handler->DispatcherServlet->HandlerAdapter處理handler->ModelAndView->DispatcherServlet->ModelAndView->ViewReslover->View->DispatcherServlet->返回給客戶

Mybatis

Mybatis原理

  • sqlsessionFactoryBuilder生成sqlsessionFactory(單例)
  • 工廠模式生成sqlsession執(zhí)行sql以及控制事務(wù)
  • Mybatis通過(guò)動(dòng)態(tài)代理使Mapper(sql映射器)接口能運(yùn)行起來(lái)即為接口生成代理對(duì)象將sql查詢到結(jié)果映射成pojo

sqlSessionFactory構(gòu)建過(guò)程

  • 解析并讀取配置中的xml創(chuàng)建Configuration對(duì)象 (單例)
  • 使用Configruation類(lèi)去創(chuàng)建sqlSessionFactory(builder模式)

Mybatis一級(jí)緩存與二級(jí)緩存

默認(rèn)情況下一級(jí)緩存是開(kāi)啟的,而且是不能關(guān)閉的。

  • 一級(jí)緩存是指 SqlSession 級(jí)別的緩存原理:使用的數(shù)據(jù)結(jié)構(gòu)是一個(gè) map,如果兩次中間出現(xiàn) commit 操作 (修改、添加、刪除),本 sqlsession 中的一級(jí)緩存區(qū)域全部清空
  • 二級(jí)緩存是指可以跨 SqlSession 的緩存。是 mapper 級(jí)別的緩存;原理:是通過(guò) CacheExecutor 實(shí)現(xiàn)的。CacheExecutor其實(shí)是 Executor 的代理對(duì)象

Zookeeper+eureka+springcloud

SpringBoot啟動(dòng)流程

  • new springApplication對(duì)象,利用spi機(jī)制加載applicationContextInitializer,applicationLister接口實(shí)例(META-INF/spring.factories);

  • 調(diào)run方法準(zhǔn)備Environment,加載應(yīng)用上下文(applicationContext),發(fā)布事件 很多通過(guò)lister實(shí)現(xiàn)

  • 創(chuàng)建spring容器, refreshContext() ,實(shí)現(xiàn)starter自動(dòng)化配置,spring.factories文件加載, bean實(shí)例化

    SpringBoot自動(dòng)配置的原理

    • @EnableAutoConfiguration找到META-INF/spring.factories(需要?jiǎng)?chuàng)建的bean在里面)配置文件
    • 讀取每個(gè)starter中的spring.factories文件

Spring Boot 的核心注解

核心注解是@SpringBootApplication 由以下三種組成

  • @SpringBootConfiguration:組合了 @Configuration 注解,實(shí)現(xiàn)配置文件的功能。
  • @EnableAutoConfiguration:打開(kāi)自動(dòng)配置的功能。
  • @ComponentScan:Spring組件掃描。

SpringBoot常用starter都有哪些

spring-boot-starter-web - Web 和 RESTful 應(yīng)用程序;spring-boot-starter-test - 單元測(cè)試和集成測(cè)試;spring-boot-starter-jdbc - 傳統(tǒng)的 JDBC;spring-boot-starter-security - 使用 SpringSecurity 進(jìn)行身份驗(yàn)證和授權(quán);spring-boot-starter-data-jpa - 帶有 Hibernate 的 Spring Data JPA;spring-boot-starter-data-rest - 使用 Spring Data REST 公布簡(jiǎn)單的 REST 服務(wù)

Spring Boot 的核心配置文件

(1):Application.yml一般用來(lái)定義單個(gè)應(yīng)用級(jí)別的,如果搭配 spring-cloud-config 使用

(2).Bootstrap.yml(先加載)系統(tǒng)級(jí)別的一些參數(shù)配置,這些參數(shù)一般是不變的

Zuul與Gateway區(qū)別

(1):zuul則是netflix公司的項(xiàng)目集成在spring-cloud中使用而已, Gateway是spring-cloud的一個(gè)子項(xiàng)目;

(2):zuul不提供異步支持流控等均由hystrix支持, gateway提供了異步支持,提供了抽象負(fù)載均衡,提供了抽象流控;理論上gateway則更適合于提高系統(tǒng)吞吐量(但不一定能有更好的性能),最終性能還需要通過(guò)嚴(yán)密的壓測(cè)來(lái)決定

(3):兩者底層實(shí)現(xiàn)都是servlet,但是gateway多嵌套了一層webflux框架

(4):zuul可用至其他微服務(wù)框架中,內(nèi)部沒(méi)有實(shí)現(xiàn)限流、負(fù)載均衡;gateway只能用在springcloud中;

Zuul原理分析

(1):請(qǐng)求給zuulservlet處理(HttpServlet子類(lèi)) zuulservlet中有一個(gè)zuulRunner對(duì)象,該對(duì)象中初始化了RequestContext(存儲(chǔ)請(qǐng)求的數(shù)據(jù)),RequestContext被所有的zuulfilter共享;

(2):zuulRunner中有 FilterProcessor(zuulfilter的管理器),其從filterloader 中獲取zuulfilter;

(3):有了這些filter之后, zuulservelet執(zhí)行的Pre-> route-> post 類(lèi)型的過(guò)濾器,如果在執(zhí)行這些過(guò)濾器有錯(cuò)誤的時(shí)候則會(huì)執(zhí)行error類(lèi)型的過(guò)濾器,執(zhí)行完后把結(jié)果返回給客戶端.

Gateway原理分析

(1):請(qǐng)求到達(dá)DispatcherHandler, DispatchHandler在IOC容器初始化時(shí)會(huì)在容器中實(shí)例化HandlerMapping接口

(2):用handlerMapping根據(jù)請(qǐng)求URL匹配到對(duì)應(yīng)的Route,然后有對(duì)應(yīng)的filter做對(duì)應(yīng)的請(qǐng)求轉(zhuǎn)發(fā)最終response返回去

Zookeeper 工作原理(待查)

Zookeeper 的核心是原子廣播,這個(gè)機(jī)制保證了各個(gè) server 之間的同步。實(shí)現(xiàn)這個(gè)機(jī)制的協(xié)議叫做 Zab 協(xié)議。Zab 協(xié)議有兩種模式,它們分別是恢復(fù)模式和廣播模式。

zoo與eur區(qū)別

  • zookeeper保證cp(一致性)
  • eureka保證ap(可用性)
  • zoo在選舉期間注冊(cè)服務(wù)癱瘓,期間不可用
  • eur各個(gè)節(jié)點(diǎn)平等關(guān)系,只要有一臺(tái)就可保證服務(wù)可用,而查詢到的數(shù)據(jù)可能不是最新的,可以很好應(yīng)對(duì)網(wǎng)絡(luò)故障導(dǎo)致部分節(jié)點(diǎn)失聯(lián)情況
  • zoo有l(wèi)eader和follower角色,eur各個(gè)節(jié)點(diǎn)平等
  • zoo采用半數(shù)存活原則(避免腦裂),eur采用自我保護(hù)機(jī)制來(lái)解決分區(qū)問(wèn)題
  • eur本質(zhì)是個(gè)工程,zoo只是一個(gè)進(jìn)程ZooKeeper基于CP,不保證高可用,如果zookeeper正在選主,或者Zookeeper集群中半數(shù)以上機(jī)器不可用,那么將無(wú)法獲得數(shù)據(jù)。Eureka基于AP,能保證高可用,即使所有機(jī)器都掛了,也能拿到本地緩存的數(shù)據(jù)。作為注冊(cè)中心,其實(shí)配置是不經(jīng)常變動(dòng)的,只有發(fā)版(發(fā)布新的版本)和機(jī)器出故障時(shí)會(huì)變。對(duì)于不經(jīng)常變動(dòng)的配置來(lái)說(shuō),CP是不合適的,而AP在遇到問(wèn)題時(shí)可以用犧牲一致性來(lái)保證可用性,既返回舊數(shù)據(jù),緩存數(shù)據(jù)。所以理論上Eureka是更適合做注冊(cè)中心。而現(xiàn)實(shí)環(huán)境中大部分項(xiàng)目可能會(huì)使用ZooKeeper,那是因?yàn)榧翰粔虼?,并且基本不?huì)遇到用做注冊(cè)中心的機(jī)器一半以上都掛了的情況。所以實(shí)際上也沒(méi)什么大問(wèn)題。

Hystrix原理(待查)

通過(guò)維護(hù)一個(gè)自己的線程池,當(dāng)線程池達(dá)到閾值的時(shí)候,就啟動(dòng)服務(wù)降級(jí),返回fallback默認(rèn)值

為什么需要hystrix熔斷

防止雪崩,及時(shí)釋放資源,防止系統(tǒng)發(fā)生更多的額級(jí)聯(lián)故障,需要對(duì)故障和延遲進(jìn)行隔離,防止單個(gè)依賴(lài)關(guān)系的失敗影響整個(gè)應(yīng)用程序;

微服務(wù)優(yōu)缺點(diǎn)

  • 每個(gè)服務(wù)高內(nèi)聚,松耦合,面向接口編程;
  • 服務(wù)間通信成本,數(shù)據(jù)一致性,多服務(wù)運(yùn)維難度增加,http傳輸效率不如rpc

eureka自我保護(hù)機(jī)制

  • eur不移除長(zhǎng)時(shí)間沒(méi)收到心跳而應(yīng)該過(guò)期的服務(wù)
  • 仍然接受新服務(wù)注冊(cè)和查詢請(qǐng)求,但是不會(huì)同步到其它節(jié)點(diǎn)(高可用)
  • 當(dāng)網(wǎng)絡(luò)穩(wěn)定后,當(dāng)前實(shí)例新注冊(cè)信息會(huì)同步到其它節(jié)點(diǎn)(最終一致性)

MQ對(duì)比

ActiveMQ:Apache出品,最早使用的消息隊(duì)列產(chǎn)品,時(shí)間比較長(zhǎng)了,最近版本更新比較緩慢。RabbitMQ:erlang語(yǔ)言開(kāi)發(fā),支持很多的協(xié)議,非常重量級(jí),更適合于企業(yè)級(jí)的開(kāi)發(fā)。性能較好,但是不利于做二次開(kāi)發(fā)和維護(hù)。RocketMQ:阿里開(kāi)源的消息中間件,純Java開(kāi)發(fā),具有高吞吐量、高可用性、適合大規(guī)模分布式系統(tǒng)應(yīng)用的特點(diǎn),分布式事務(wù)。ZeroMQ:號(hào)稱(chēng)最快的消息隊(duì)列系統(tǒng),尤其針對(duì)大吞吐量的需求場(chǎng)景,采用 C 語(yǔ)言實(shí)現(xiàn)。消息隊(duì)列的選型需要根據(jù)具體應(yīng)用需求而定,ZeroMQ 小而美,RabbitMQ 大而穩(wěn),Kakfa 和 RocketMQ 快而強(qiáng)勁

JAVA基礎(chǔ)

AVL樹(shù)與紅黑樹(shù)(R-B樹(shù))的區(qū)別與聯(lián)系

  • AVL是嚴(yán)格的平衡樹(shù),因此在增加或者刪除節(jié)點(diǎn)的時(shí)候,根據(jù)不同情況,旋轉(zhuǎn)的次數(shù)比紅黑樹(shù)要多;
  • 紅黑樹(shù)是用非嚴(yán)格的平衡來(lái)?yè)Q取增刪節(jié)點(diǎn)時(shí)候旋轉(zhuǎn)次數(shù)的降低開(kāi)銷(xiāo);
  • 所以簡(jiǎn)單說(shuō),查詢多選擇AVL樹(shù),查詢更新次數(shù)差不多選紅黑樹(shù)
  • AVL樹(shù)順序插入和刪除時(shí)有20%左右的性能優(yōu)勢(shì),紅黑樹(shù)隨機(jī)操作15%左右優(yōu)勢(shì),現(xiàn)實(shí)應(yīng)用當(dāng)然一般都是隨機(jī)情況,所以紅黑樹(shù)得到了更廣泛的應(yīng)用索引為B+樹(shù)Hashmap為紅黑樹(shù)

為啥redis zset使用跳躍鏈表而不用紅黑樹(shù)實(shí)現(xiàn)

  • skiplist的復(fù)雜度和紅黑樹(shù)一樣,而且實(shí)現(xiàn)起來(lái)更簡(jiǎn)單。
  • 在并發(fā)環(huán)境下紅黑樹(shù)在插入和刪除時(shí)需要rebalance,性能不如跳表。

JAVA基本數(shù)據(jù)類(lèi)型

(1個(gè)字節(jié)是8個(gè)bit)整數(shù)型:byte(1字節(jié))、short(2字節(jié))、int(4字節(jié))、long(8字節(jié))浮點(diǎn)型:float(4字節(jié))、double(8字節(jié))布爾型:boolean(1字節(jié))字符型:char(2字節(jié))

IO與NIO

包括 類(lèi)File,outputStream,inputStream,writer,readerseralizable(5類(lèi)1接口)

NIO三大核心內(nèi)容 selector(選擇器,用于監(jiān)聽(tīng)channel),channel(通道),buffer(緩沖區(qū))

NIO與IO區(qū)別,IO面向流,NIO面向緩沖區(qū);io阻塞,nio非阻塞

異常類(lèi)

throwable為父類(lèi),子為error跟exception,exception分runtime(空指針,越界等)跟checkexception(sql,io,找不到類(lèi)等異常)

LVS(4層與7層)原理

  • 由前端虛擬負(fù)載均衡器和后端真實(shí)服務(wù)器群組成;
  • 請(qǐng)求發(fā)送給虛擬服務(wù)器后其根據(jù)包轉(zhuǎn)發(fā)策略以及負(fù)載均衡調(diào)度算法轉(zhuǎn)發(fā)給真實(shí)服務(wù)器
  • 所謂四層(lvs,f5)就是基于IP+端口的負(fù)載均衡;七層(nginx)就是基于URL等應(yīng)用層信息的負(fù)載均衡

StringBuilder與StringBuffer

  • StringBuilder 更快;
  • StringBuffer是線程安全的

interrupt/isInterrupted/interrupt區(qū)別

  • interrupt() 調(diào)用該方法的線程的狀態(tài)為將被置為'中斷'狀態(tài)(set操作)
  • isinterrupted() 是作用于調(diào)用該方法的線程對(duì)象所對(duì)應(yīng)的線程的中斷信號(hào)是true還是false(get操作)。例如我們可以在A線程中去調(diào)用B線程對(duì)象的isInterrupted方法,查看的是A
  • interrupted()是靜態(tài)方法:內(nèi)部實(shí)現(xiàn)是調(diào)用的當(dāng)前線程的isInterrupted(),并且會(huì)重置當(dāng)前線程的中斷狀態(tài)(getandset)

sleep與wait區(qū)別

sleep屬于線程類(lèi),wait屬于object類(lèi);sleep不釋放鎖

CountDownLatch和CyclicBarrier區(qū)別

  • con用于主線程等待其他子線程任務(wù)都執(zhí)行完畢后再執(zhí)行,cyc用于一組線程相互等待大家都達(dá)到某個(gè)狀態(tài)后,再同時(shí)執(zhí)行;
  • CountDownLatch是不可重用的,CyclicBarrier可重用

終止線程方法

  • 使用退出標(biāo)志,說(shuō)線程正常退出;
  • 通過(guò)判斷this.interrupted() throw new InterruptedException()來(lái)停止使用String常量池作為鎖對(duì)象會(huì)導(dǎo)致兩個(gè)線程持有相同的鎖,另一個(gè)線程不執(zhí)行,改用其他如new Object()

ThreadLocal的原理和應(yīng)用

原理:

線程中創(chuàng)建副本,訪問(wèn)自己內(nèi)部的副本變量,內(nèi)部實(shí)現(xiàn)是其內(nèi)部類(lèi)名叫ThreadLocalMap的成員變量threadLocals,key為本身,value為實(shí)際存值的變量副本

應(yīng)用:

  • 用來(lái)解決數(shù)據(jù)庫(kù)連接,存放connection對(duì)象,不同線程存放各自session;
  • 解決simpleDateFormat線程安全問(wèn)題;
  • 會(huì)出現(xiàn)內(nèi)存泄漏,顯式remove..不要與線程池配合,因?yàn)閣orker往往是不會(huì)退出的;

threadLocal 內(nèi)存泄漏問(wèn)題

如果是強(qiáng)引用,設(shè)置tl=null,但是key的引用依然指向ThreadLocal對(duì)象,所以會(huì)有內(nèi)存泄漏,而使用弱引用則不會(huì);但是還是會(huì)有內(nèi)存泄漏存在,ThreadLocal被回收,key的值變成null,導(dǎo)致整個(gè)value再也無(wú)法被訪問(wèn)到;解決辦法:在使用結(jié)束時(shí),調(diào)用ThreadLocal.remove來(lái)釋放其value的引用;

如果我們要獲取父線程的ThreadLocal值呢

ThreadLocal是不具備繼承性的,所以是無(wú)法獲取到的,但是我們可以用InteritableThreadLocal來(lái)實(shí)現(xiàn)這個(gè)功能。InteritableThreadLocal繼承來(lái)ThreadLocal,重寫(xiě)了createdMap方法,已經(jīng)對(duì)應(yīng)的get和set方法,不是在利用了threadLocals,而是interitableThreadLocals變量。

這個(gè)變量會(huì)在線程初始化的時(shí)候(調(diào)用init方法),會(huì)判斷父線程的interitableThreadLocals變量是否為空,如果不為空,則把放入子線程中,但是其實(shí)這玩意沒(méi)啥鳥(niǎo)用,當(dāng)父線程創(chuàng)建完子線程后,如果改變父線程內(nèi)容是同步不到子線程的。。。同樣,如果在子線程創(chuàng)建完后,再去賦值,也是沒(méi)啥鳥(niǎo)用的

線程狀態(tài)

線程池有5種狀態(tài):running,showdown,stop,Tidying,TERMINATED。

  • running:線程池處于運(yùn)行狀態(tài),可以接受任務(wù),執(zhí)行任務(wù),創(chuàng)建線程默認(rèn)就是這個(gè)狀態(tài)了

  • showdown:調(diào)用showdown()函數(shù),不會(huì)接受新任務(wù),但是會(huì)慢慢處理完堆積的任務(wù)。

  • stop:調(diào)用showdownnow()函數(shù),不會(huì)接受新任務(wù),不處理已有的任務(wù),會(huì)中斷現(xiàn)有的任務(wù)。

  • Tidying:當(dāng)線程池狀態(tài)為showdown或者stop,任務(wù)數(shù)量為0,就會(huì)變?yōu)閠idying。這個(gè)時(shí)候會(huì)調(diào)用鉤子函數(shù)terminated()。

  • TERMINATED:terminated()執(zhí)行完成。

在線程池中,用了一個(gè)原子類(lèi)來(lái)記錄線程池的信息,用了int的高3位表示狀態(tài),后面的29位表示線程池中線程的個(gè)數(shù)。

Java中的線程池是如何實(shí)現(xiàn)的?

  • 線程中線程被抽象為靜態(tài)內(nèi)部類(lèi)Worker,是基于AQS實(shí)現(xiàn)的存放在HashSet中;
  • 要被執(zhí)行的線程存放在BlockingQueue中;
  • 基本思想就是從workQueue中取出要執(zhí)行的任務(wù),放在worker中處理;

如果線程池中的一個(gè)線程運(yùn)行時(shí)出現(xiàn)了異常,會(huì)發(fā)生什么

如果提交任務(wù)的時(shí)候使用了submit,則返回的feature里會(huì)存有異常信息,但是如果數(shù)execute則會(huì)打印出異常棧。但是不會(huì)給其他線程造成影響。之后線程池會(huì)刪除該線程,會(huì)新增加一個(gè)worker。

線程池原理

  • 提交一個(gè)任務(wù),線程池里存活的核心線程數(shù)小于corePoolSize時(shí),線程池會(huì)創(chuàng)建一個(gè)核心線程去處理提交的任務(wù)
  • 如果線程池核心線程數(shù)已滿,即線程數(shù)已經(jīng)等于corePoolSize,一個(gè)新提交的任務(wù),會(huì)被放進(jìn)任務(wù)隊(duì)列workQueue排隊(duì)等待執(zhí)行。
  • 當(dāng)線程池里面存活的線程數(shù)已經(jīng)等于corePoolSize了,并且任務(wù)隊(duì)列workQueue也滿,判斷線程數(shù)是否達(dá)到maximumPoolSize,即最大線程數(shù)是否已滿,如果沒(méi)到達(dá),創(chuàng)建非核心線程執(zhí)行提交的任務(wù)。
  • 如果當(dāng)前的線程數(shù)達(dá)到了maximumPoolSize,還有新的任務(wù)過(guò)來(lái)的話,直接采用拒絕策略處理。

拒絕策略

  • AbortPolicy直接拋出異常阻止線程運(yùn)行;
  • CallerRunsPolicy如果被丟棄的線程任務(wù)未關(guān)閉,則執(zhí)行該線程;
  • DiscardOldestPolicy移除隊(duì)列最早線程嘗試提交當(dāng)前任務(wù)
  • DiscardPolicy丟棄當(dāng)前任務(wù),不做處理

newFixedThreadPool (固定數(shù)目線程的線程池)

  • 阻塞隊(duì)列為無(wú)界隊(duì)列LinkedBlockingQueue
  • 適用于處理CPU密集型的任務(wù),適用執(zhí)行長(zhǎng)期的任務(wù)

newCachedThreadPool(可緩存線程的線程池)

  • 阻塞隊(duì)列是SynchronousQueue
  • 適用于并發(fā)執(zhí)行大量短期的小任務(wù)

newSingleThreadExecutor(單線程的線程池)

  • 阻塞隊(duì)列是LinkedBlockingQueue
  • 適用于串行執(zhí)行任務(wù)的場(chǎng)景,一個(gè)任務(wù)一個(gè)任務(wù)地執(zhí)行

newScheduledThreadPool(定時(shí)及周期執(zhí)行的線程池)

  • 阻塞隊(duì)列是DelayedWorkQueue
  • 周期性執(zhí)行任務(wù)的場(chǎng)景,需要限制線程數(shù)量的場(chǎng)景

java鎖相關(guān)

synchronized實(shí)現(xiàn)原理

contentionList(請(qǐng)求鎖線程隊(duì)列)entryList(有資格的候選者隊(duì)列)waitSet(wait方法后阻塞隊(duì)列)onDeck(競(jìng)爭(zhēng)候選者)ower(競(jìng)爭(zhēng)到鎖線程)!ower(執(zhí)行成功釋放鎖后狀態(tài));Synchronized 是非公平鎖。

Synchronized 在線程進(jìn)入 ContentionList 時(shí),等待的線程會(huì)先嘗試自旋獲取鎖,如果獲取不到就進(jìn)入 ContentionList,這明顯對(duì)于已經(jīng)進(jìn)入隊(duì)列的線程是不公平的,還有一個(gè)不公平的事情就是自旋獲取鎖的線程還可能直接搶占 OnDeck 線程的鎖資源。

底層是由一對(duì)monitorenter和monitorexit指令實(shí)現(xiàn)的(監(jiān)視器鎖)

每個(gè)對(duì)象有一個(gè)監(jiān)視器鎖(monitor)。當(dāng)monitor被占用時(shí)就會(huì)處于鎖定狀態(tài),線程執(zhí)行monitorenter指令時(shí)嘗試獲取monitor的所有權(quán),過(guò)程:

  • 如果monitor的進(jìn)入數(shù)為0,則該線程進(jìn)入monitor,然后將進(jìn)入數(shù)設(shè)置為1,該線程即為monitor的所有者。
  • 如果線程已經(jīng)占有該monitor,只是重新進(jìn)入,則進(jìn)入monitor的進(jìn)入數(shù)加1.
  • 如果其他線程已經(jīng)占用了monitor,則該線程進(jìn)入阻塞狀態(tài),直到monitor的進(jìn)入數(shù)為0,再重新嘗試獲取monitor的所有權(quán)。

ReentrantLock 是如何實(shí)現(xiàn)可重入性的 ?

內(nèi)部自定義了同步器 Sync,加鎖的時(shí)候通過(guò)CAS 算法 ,將線程對(duì)象放到一個(gè)雙向鏈表 中,每次獲取鎖的時(shí)候 ,看下當(dāng)前維 護(hù)的那個(gè)線程ID和當(dāng)前請(qǐng)求的線程ID是否一樣,一樣就可重入了;

ReentrantLock如何避免死鎖?

  • 響應(yīng)中斷l(xiāng)ockInterruptibly()
  • 可輪詢鎖tryLock()
  • 定時(shí)鎖tryLock(long time)

tryLock 和 lock 和 lockInterruptibly 的區(qū)別

(1):tryLock 能獲得鎖就返回 true,不能就立即返回 false,

(2):tryLock(long timeout,TimeUnit unit),可以增加時(shí)間限制,如果超過(guò)該時(shí)間段還沒(méi)獲得鎖,返回 false

(3):lock 能獲得鎖就返回 true,不能的話一直等待獲得鎖

(4):lock 和 lockInterruptibly,如果兩個(gè)線程分別執(zhí)行這兩個(gè)方法,但此時(shí)中斷這兩個(gè)線程, lock 不會(huì)拋出異常,而 lockInterruptibly 會(huì)拋出異常。

CountDownLatch和CyclicBarrier的區(qū)別是什么

CountDownLatch是等待其他線程執(zhí)行到某一個(gè)點(diǎn)的時(shí)候,在繼續(xù)執(zhí)行邏輯(子線程不會(huì)被阻塞,會(huì)繼續(xù)執(zhí)行),只能被使用一次。最常見(jiàn)的就是join形式,主線程等待子線程執(zhí)行完任務(wù),在用主線程去獲取結(jié)果的方式(當(dāng)然不一定),內(nèi)部是用計(jì)數(shù)器相減實(shí)現(xiàn)的(沒(méi)錯(cuò),又特么是AQS),AQS的state承擔(dān)了計(jì)數(shù)器的作用,初始化的時(shí)候,使用CAS賦值,主線程調(diào)用await()則被加入共享線程等待隊(duì)列里面,子線程調(diào)用countDown的時(shí)候,使用自旋的方式,減1,知道為0,就觸發(fā)喚醒。

CyclicBarrier回環(huán)屏障,主要是等待一組線程到底同一個(gè)狀態(tài)的時(shí)候,放閘。CyclicBarrier還可以傳遞一個(gè)Runnable對(duì)象,可以到放閘的時(shí)候,執(zhí)行這個(gè)任務(wù)。CyclicBarrier是可循環(huán)的,當(dāng)調(diào)用await的時(shí)候如果count變成0了則會(huì)重置狀態(tài),如何重置呢,CyclicBarrier新增了一個(gè)字段parties,用來(lái)保存初始值,當(dāng)count變?yōu)?的時(shí)候,就重新賦值。還有一個(gè)不同點(diǎn),CyclicBarrier不是基于AQS的,而是基于RentrantLock實(shí)現(xiàn)的。存放的等待隊(duì)列是用了條件變量的方式。

synchronized與ReentrantLock區(qū)別

  • 都是可重入鎖;R是顯示獲取和釋放鎖,s是隱式;
  • R更靈活可以知道有沒(méi)有成功獲取鎖,可以定義讀寫(xiě)鎖,是api級(jí)別,s是JVM級(jí)別;
  • R可以定義公平鎖;Lock是接口,s是java中的關(guān)鍵字

什么是信號(hào)量Semaphore

信號(hào)量是一種固定資源的限制的一種并發(fā)工具包,基于AQS實(shí)現(xiàn)的,在構(gòu)造的時(shí)候會(huì)設(shè)置一個(gè)值,代表著資源數(shù)量。信號(hào)量主要是應(yīng)用于是用于多個(gè)共享資源的互斥使用,和用于并發(fā)線程數(shù)的控制(druid的數(shù)據(jù)庫(kù)連接數(shù),就是用這個(gè)實(shí)現(xiàn)的),信號(hào)量也分公平和非公平的情況,基本方式和reentrantLock差不多,在請(qǐng)求資源調(diào)用task時(shí),會(huì)用自旋的方式減1,如果成功,則獲取成功了,如果失敗,導(dǎo)致資源數(shù)變?yōu)榱?,就會(huì)加入隊(duì)列里面去等待。調(diào)用release的時(shí)候會(huì)加一,補(bǔ)充資源,并喚醒等待隊(duì)列。

Semaphore 應(yīng)用

  • acquire() release() 可用于對(duì)象池,資源池的構(gòu)建,比如靜態(tài)全局對(duì)象池,數(shù)據(jù)庫(kù)連接池;
  • 可創(chuàng)建計(jì)數(shù)為1的S,作為互斥鎖(二元信號(hào)量)

可重入鎖概念

(1):可重入鎖是指同一個(gè)線程可以多次獲取同一把鎖,不會(huì)因?yàn)橹耙呀?jīng)獲取過(guò)還沒(méi)釋放而阻塞;

(2):reentrantLock和synchronized都是可重入鎖

(3):可重入鎖的一個(gè)優(yōu)點(diǎn)是可一定程度避免死鎖

ReentrantLock原理(CAS+AQS)

CAS+AQS隊(duì)列來(lái)實(shí)現(xiàn)

(1):先通過(guò)CAS嘗試獲取鎖, 如果此時(shí)已經(jīng)有線程占據(jù)了鎖,那就加入AQS隊(duì)列并且被掛起;

(2):當(dāng)鎖被釋放之后, 排在隊(duì)首的線程會(huì)被喚醒CAS再次嘗試獲取鎖,

(3):如果是非公平鎖, 同時(shí)還有另一個(gè)線程進(jìn)來(lái)嘗試獲取可能會(huì)讓這個(gè)線程搶到鎖;

(4):如果是公平鎖, 會(huì)排到隊(duì)尾,由隊(duì)首的線程獲取到鎖。

AQS 原理

Node內(nèi)部類(lèi)構(gòu)成的一個(gè)雙向鏈表結(jié)構(gòu)的同步隊(duì)列,通過(guò)控制(volatile的int類(lèi)型)state狀態(tài)來(lái)判斷鎖的狀態(tài),對(duì)于非可重入鎖狀態(tài)不是0則去阻塞;

對(duì)于可重入鎖如果是0則執(zhí)行,非0則判斷當(dāng)前線程是否是獲取到這個(gè)鎖的線程,是的話把state狀態(tài)+1,比如重入5次,那么state=5。而在釋放鎖的時(shí)候,同樣需要釋放5次直到state=0其他線程才有資格獲得鎖

AQS兩種資源共享方式

  • Exclusive:獨(dú)占,只有一個(gè)線程能執(zhí)行,如ReentrantLock
  • Share:共享,多個(gè)線程可以同時(shí)執(zhí)行,如Semaphore、CountDownLatch、ReadWriteLock,CyclicBarrier

CAS原理

內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B,當(dāng)A=V時(shí),將內(nèi)存值修改為B,否則什么都不做;

CAS的缺點(diǎn):

(1):ABA問(wèn)題;(2):如果CAS失敗,自旋會(huì)給CPU帶來(lái)壓力;(3):只能保證對(duì)一個(gè)變量的原子性操作,i++這種是不能保證的

CAS在java中的應(yīng)用:

(1):Atomic系列

公平鎖與分公平鎖

(1):公平鎖指在分配鎖前檢查是否有線程在排隊(duì)等待獲取該鎖,優(yōu)先分配排隊(duì)時(shí)間最長(zhǎng)的線程,非公平直接嘗試獲取鎖(2):公平鎖需多維護(hù)一個(gè)鎖線程隊(duì)列,效率低;默認(rèn)非公平

獨(dú)占鎖與共享鎖

(1):ReentrantLock為獨(dú)占鎖(悲觀加鎖策略)(2):ReentrantReadWriteLock中讀鎖為共享鎖(3):JDK1.8 郵戳鎖(StampedLock), 不可重入鎖讀的過(guò)程中也允許獲取寫(xiě)鎖后寫(xiě)入!這樣一來(lái),我們讀的數(shù)據(jù)就可能不一致,所以,需要一點(diǎn)額外的代碼來(lái)判斷讀的過(guò)程中是否有寫(xiě)入,這種讀鎖是一種樂(lè)觀鎖, 樂(lè)觀鎖的并發(fā)效率更高,但一旦有小概率的寫(xiě)入導(dǎo)致讀取的數(shù)據(jù)不一致,需要能檢測(cè)出來(lái),再讀一遍就行

4種鎖狀態(tài)

  • 無(wú)鎖

  • 偏向鎖會(huì)偏向第一個(gè)訪問(wèn)鎖的線程,當(dāng)一個(gè)線程訪問(wèn)同步代碼塊獲得鎖時(shí),會(huì)在對(duì)象頭和棧幀記錄里存儲(chǔ)鎖偏向的線程ID,當(dāng)這個(gè)線程再次進(jìn)入同步代碼塊時(shí),就不需要CAS操作來(lái)加鎖了,只要測(cè)試一下對(duì)象頭里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖如果偏向鎖未啟動(dòng),new出的對(duì)象是普通對(duì)象(即無(wú)鎖,有稍微競(jìng)爭(zhēng)會(huì)成輕量級(jí)鎖),如果啟動(dòng),new出的對(duì)象是匿名偏向(偏向鎖)對(duì)象頭主要包括兩部分?jǐn)?shù)據(jù):Mark Word(標(biāo)記字段, 存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù))、class Pointer(類(lèi)型指針, 是對(duì)象指向它的類(lèi)元數(shù)據(jù)的指針)

  • 輕量級(jí)鎖(自旋鎖)(1):在把線程進(jìn)行阻塞操作之前先讓線程自旋等待一段時(shí)間,可能在等待期間其他線程已經(jīng) 解鎖,這時(shí)就無(wú)需再讓線程執(zhí)行阻塞操作,避免了用戶態(tài)到內(nèi)核態(tài)的切換。(自適應(yīng)自旋時(shí)間為一個(gè)線程上下文切換的時(shí)間)

  • (2):在用自旋鎖時(shí)有可能造成死鎖,當(dāng)遞歸調(diào)用時(shí)有可能造成死鎖

  • (3):自旋鎖底層是通過(guò)指向線程棧中Lock Record的指針來(lái)實(shí)現(xiàn)的

  • 重量級(jí)鎖

輕量級(jí)鎖與偏向鎖的區(qū)別

(1):輕量級(jí)鎖是通過(guò)CAS來(lái)避免進(jìn)入開(kāi)銷(xiāo)較大的互斥操作

(2):偏向鎖是在無(wú)競(jìng)爭(zhēng)場(chǎng)景下完全消除同步,連CAS也不執(zhí)行

自旋鎖升級(jí)到重量級(jí)鎖條件

(1):某線程自旋次數(shù)超過(guò)10次;

(2):等待的自旋線程超過(guò)了系統(tǒng)core數(shù)的一半;

讀寫(xiě)鎖了解嘛,知道讀寫(xiě)鎖的實(shí)現(xiàn)方式嘛

常用的讀寫(xiě)鎖ReentrantReanWritelock,這個(gè)其實(shí)和reentrantLock相似,也是基于AQS的,但是這個(gè)是基于共享資源的,不是互斥,關(guān)鍵在于state的處理,讀寫(xiě)鎖把高16為記為讀狀態(tài),低16位記為寫(xiě)狀態(tài),就分開(kāi)了,讀讀情況其實(shí)就是讀鎖重入,讀寫(xiě)/寫(xiě)讀/寫(xiě)寫(xiě)都是互斥的,只要判斷低16位就好了。

zookeeper實(shí)現(xiàn)分布式鎖

(1):利用節(jié)點(diǎn)名稱(chēng)唯一性來(lái)實(shí)現(xiàn),加鎖時(shí)所有客戶端一起創(chuàng)建節(jié)點(diǎn),只有一個(gè)創(chuàng)建成功者獲得鎖,解鎖時(shí)刪除節(jié)點(diǎn)。

(2):利用臨時(shí)順序節(jié)點(diǎn)實(shí)現(xiàn),加鎖時(shí)所有客戶端都創(chuàng)建臨時(shí)順序節(jié)點(diǎn),創(chuàng)建節(jié)點(diǎn)序列號(hào)最小的獲得鎖,否則監(jiān)視比自己序列號(hào)次小的節(jié)點(diǎn)進(jìn)行等待

(3):方案2比1好處是當(dāng)zookeeper宕機(jī)后,臨時(shí)順序節(jié)點(diǎn)會(huì)自動(dòng)刪除釋放鎖,不會(huì)造成鎖等待;

(4):方案1會(huì)產(chǎn)生驚群效應(yīng)(當(dāng)有很多進(jìn)程在等待鎖的時(shí)候,在釋放鎖的時(shí)候會(huì)有很多進(jìn)程就過(guò)來(lái)爭(zhēng)奪鎖)。

(5):由于需要頻繁創(chuàng)建和刪除節(jié)點(diǎn),性能上不如redis鎖

volatile變量

(1):變量可見(jiàn)性

(2):防止指令重排序

(3):保障變量單次讀,寫(xiě)操作的原子性,但不能保證i++這種操作的原子性,因?yàn)楸举|(zhì)是讀,寫(xiě)兩次操作

volatile如何保證線程間可見(jiàn)和避免指令重排

volatile可見(jiàn)性是有指令原子性保證的,在jmm中定義了8類(lèi)原子性指令,比如write,store,read,load。而volatile就要求write-store,load-read成為一個(gè)原子性操作,這樣子可以確保在讀取的時(shí)候都是從主內(nèi)存讀入,寫(xiě)入的時(shí)候會(huì)同步到主內(nèi)存中(準(zhǔn)確來(lái)說(shuō)也是內(nèi)存屏障),指令重排則是由內(nèi)存屏障來(lái)保證的,由兩個(gè)內(nèi)存屏障:

  • 一個(gè)是編譯器屏障:阻止編譯器重排,保證編譯程序時(shí)在優(yōu)化屏障之前的指令不會(huì)在優(yōu)化屏障之后執(zhí)行。
  • 第二個(gè)是cpu屏障:sfence保證寫(xiě)入,lfence保證讀取,lock類(lèi)似于鎖的方式。java多執(zhí)行了一個(gè)“l(fā)oad addl $0x0, (%esp)”操作,這個(gè)操作相當(dāng)于一個(gè)lock指令,就是增加一個(gè)完全的內(nèi)存屏障指令。

JVM

jre、jdk、jvm的關(guān)系:

jdk是最小的開(kāi)發(fā)環(huán)境,由jre++java工具組成。

jre是java運(yùn)行的最小環(huán)境,由jvm+核心類(lèi)庫(kù)組成。

jvm是虛擬機(jī),是java字節(jié)碼運(yùn)行的容器,如果只有jvm是無(wú)法運(yùn)行java的,因?yàn)槿鄙倭撕诵念?lèi)庫(kù)。

JVM內(nèi)存模型

(1):堆<對(duì)象,靜態(tài)變量,共享

(2):方法區(qū)<存放類(lèi)信息,常量池,共享>(java8移除了永久代(PermGen),替換為元空間(Metaspace))

(3):虛擬機(jī)棧<線程執(zhí)行方法的時(shí)候內(nèi)部存局部變量會(huì)存堆中對(duì)象的地址等等數(shù)據(jù)>

(4):本地方法棧<存放各種native方法的局部變量表之類(lèi)的信息>

(5):程序計(jì)數(shù)器<記錄當(dāng)前線程執(zhí)行到哪一條字節(jié)碼指令位置>

對(duì)象4種引用

(1):強(qiáng)(內(nèi)存泄露主因)

(2):軟(只有軟引用的話,空間不足將被回收),適合緩存用

(3):弱(只,GC會(huì)回收)

(4):虛引用(用于跟蹤GC狀態(tài))用于管理堆外內(nèi)存

對(duì)象的構(gòu)成:

一個(gè)對(duì)象分為3個(gè)區(qū)域:對(duì)象頭、實(shí)例數(shù)據(jù)、對(duì)齊填充

對(duì)象頭:主要是包括兩部分,1.存儲(chǔ)自身的運(yùn)行時(shí)數(shù)據(jù)比如hash碼,分代年齡,鎖標(biāo)記等(但是不是絕對(duì)哦,鎖狀態(tài)如果是偏向鎖,輕量級(jí)鎖,是沒(méi)有hash碼的。。。是不固定的)2.指向類(lèi)的元數(shù)據(jù)指針。還有可能存在第三部分,那就是數(shù)組類(lèi)型,會(huì)多一塊記錄數(shù)組的長(zhǎng)度(因?yàn)閿?shù)組的長(zhǎng)度是jvm判斷不出來(lái)的,jvm只有元數(shù)據(jù)信息)

實(shí)例數(shù)據(jù):會(huì)根據(jù)虛擬機(jī)分配策略來(lái)定,分配策略中,會(huì)把相同大小的類(lèi)型放在一起,并按照定義順序排列(父類(lèi)的變量也會(huì)在哦)

對(duì)齊填充:這個(gè)意義不是很大,主要在虛擬機(jī)規(guī)范中對(duì)象必須是8字節(jié)的整數(shù),所以當(dāng)對(duì)象不滿足這個(gè)情況時(shí),就會(huì)用占位符填充

如果判斷一個(gè)對(duì)象是否存活:

一般判斷對(duì)象是否存活有兩種算法,一種是引用計(jì)數(shù),另外一種是可達(dá)性分析。在java中主要是第二種

java是根據(jù)什么來(lái)執(zhí)行可達(dá)性分析的:

根據(jù)GC ROOTS。GC ROOTS可以的對(duì)象有:虛擬機(jī)棧中的引用對(duì)象,方法區(qū)的類(lèi)變量的引用,方法區(qū)中的常量引用,本地方法棧中的對(duì)象引用。

JVM 類(lèi)加載順序

(1):加載獲取類(lèi)的二進(jìn)制字節(jié)流,將其靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)

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

(3):準(zhǔn)備在方法區(qū)中對(duì)類(lèi)的static變量分配內(nèi)存并設(shè)置類(lèi)變量數(shù)據(jù)類(lèi)型默認(rèn)的初始值,不包括實(shí)例變量,實(shí)例變量將會(huì)在對(duì)象實(shí)例化的時(shí)候隨著對(duì)象一起分配在Java堆中

(4):解析將常量池內(nèi)的符號(hào)引用替換為直接引用的過(guò)程

(5):初始化為類(lèi)的靜態(tài)變量賦予正確的初始值(Java代碼中被顯式地賦予的值)

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

(1):?jiǎn)?dòng)類(lèi)加載器(home)加載jvm核心類(lèi)庫(kù),如java.lang.*等

(2):擴(kuò)展類(lèi)加載器(ext),父加載器為啟動(dòng)類(lèi)加載器,從jre/lib/ext下加載類(lèi)庫(kù)

(3):應(yīng)用程序類(lèi)加載器(用戶classpath路徑)父加載器為擴(kuò)展類(lèi)加載器,從環(huán)境變量中加載類(lèi)

雙親委派機(jī)制

(1):類(lèi)加載器收到類(lèi)加載的請(qǐng)求

(2):把這個(gè)請(qǐng)求委托給父加載器去完成,一直向上委托,直到啟動(dòng)類(lèi)加載器

(3):?jiǎn)?dòng)器加載器檢查能不能加載,能就加載(結(jié)束);否則,拋出異常,通知子加載器進(jìn)行加載

(4):保障類(lèi)的唯一性和安全性以及保證JDK核心類(lèi)的優(yōu)先加載

雙親委派模型有啥作用:

保證java基礎(chǔ)類(lèi)在不同的環(huán)境還是同一個(gè)Class對(duì)象,避免出現(xiàn)了自定義類(lèi)覆蓋基礎(chǔ)類(lèi)的情況,導(dǎo)致出現(xiàn)安全問(wèn)題。還可以避免類(lèi)的重復(fù)加載。

如何打破雙親委派模型?

(1):自定義類(lèi)加載器,繼承ClassLoader類(lèi)重寫(xiě)loadClass方法;

(2):SPI

tomcat是如何打破雙親委派模型:

tomcat有著特殊性,它需要容納多個(gè)應(yīng)用,需要做到應(yīng)用級(jí)別的隔離,而且需要減少重復(fù)性加載,所以劃分為:/common 容器和應(yīng)用共享的類(lèi)信息,/server容器本身的類(lèi)信息,/share應(yīng)用通用的類(lèi)信息,/WEB-INF/lib應(yīng)用級(jí)別的類(lèi)信息。整體可以分為:boostrapClassLoader->ExtensionClassLoader->ApplicationClassLoader->CommonClassLoader->CatalinaClassLoader(容器本身的加載器)/ShareClassLoader(共享的)->WebAppClassLoader。雖然第一眼是滿足雙親委派模型的,但是不是的,因?yàn)殡p親委派模型是要先提交給父類(lèi)裝載,而tomcat是優(yōu)先判斷是否是自己負(fù)責(zé)的文件位置,進(jìn)行加載的。

SPI:(Service Provider interface)

(1):服務(wù)提供接口(服務(wù)發(fā)現(xiàn)機(jī)制):

(2):通過(guò)加載ClassPath下META_INF/services,自動(dòng)加載文件里所定義的類(lèi)

(3):通過(guò)ServiceLoader.load/Service.providers方法通過(guò)反射拿到實(shí)現(xiàn)類(lèi)的實(shí)例

SPI應(yīng)用?

(1):應(yīng)用于JDBC獲取數(shù)據(jù)庫(kù)驅(qū)動(dòng)連接過(guò)程就是應(yīng)用這一機(jī)制

(2):apache最早提供的common-logging只有接口.沒(méi)有實(shí)現(xiàn)..發(fā)現(xiàn)日志的提供商通過(guò)SPI來(lái)具體找到日志提供商實(shí)現(xiàn)類(lèi)

雙親委派機(jī)制缺陷?

(1):雙親委派核心是越基礎(chǔ)的類(lèi)由越上層的加載器進(jìn)行加載, 基礎(chǔ)的類(lèi)總是作為被調(diào)用代碼調(diào)用的API,無(wú)法實(shí)現(xiàn)基礎(chǔ)類(lèi)調(diào)用用戶的代碼….

(2):JNDI服務(wù)它的代碼由啟動(dòng)類(lèi)加載器去加載,但是他需要調(diào)獨(dú)立廠商實(shí)現(xiàn)的應(yīng)用程序,如何解決?線程上下文件類(lèi)加載器(Thread Context ClassLoader), JNDI服務(wù)使用這個(gè)線程上下文類(lèi)加載器去加載所需要的SPI代碼,也就是父類(lèi)加載器請(qǐng)求子類(lèi)加載器去完成類(lèi)加載動(dòng)作Java中所有涉及SPI的加載動(dòng)作基本上都采用這種方式,例如JNDI,JDBC

導(dǎo)致fullGC的原因

(1):老年代空間不足

(2):永久代(方法區(qū))空間不足

(3):顯式調(diào)用system.gc()

堆外內(nèi)存的優(yōu)缺點(diǎn)

Ehcache中的一些版本,各種 NIO 框架,Dubbo,Memcache 等中會(huì)用到,NIO包下ByteBuffer來(lái)創(chuàng)建堆外內(nèi)存堆外內(nèi)存,其實(shí)就是不受JVM控制的內(nèi)存。

相比于堆內(nèi)內(nèi)存有幾個(gè)優(yōu)勢(shì):

減少了垃圾回收的工作,因?yàn)槔厥諘?huì)暫停其他的工作。加快了復(fù)制的速度。因?yàn)槎褍?nèi)在 flush 到遠(yuǎn)程時(shí),會(huì)先復(fù)制到直接內(nèi)存(非堆內(nèi)存),然后在發(fā)送;而堆外內(nèi)存相當(dāng)于省略掉了復(fù)制這項(xiàng)工作??梢詳U(kuò)展至更大的內(nèi)存空間。比如超過(guò) 1TB 甚至比主存還大的空間。

缺點(diǎn)總結(jié)如下:

堆外內(nèi)存難以控制,如果內(nèi)存泄漏,那么很難排查,通過(guò)-XX:MaxDirectMemerySize來(lái)指定,當(dāng)達(dá)到閾值的時(shí)候,調(diào)用system.gc來(lái)進(jìn)行一次full gc堆外內(nèi)存相對(duì)來(lái)說(shuō),不適合存儲(chǔ)很復(fù)雜的對(duì)象。一般簡(jiǎn)單的對(duì)象或者扁平化的比較適合jstat查看內(nèi)存回收概況,實(shí)時(shí)查看各個(gè)分區(qū)的分配回收情況,jmap查看內(nèi)存棧,查看內(nèi)存中對(duì)象占用大小,jstack查看線程棧,死鎖,性能瓶頸

JVM七種垃圾收集器

(1):Serial 收集器 復(fù)制算法,單線程,新生代)

(2):ParNew 收集器(復(fù)制算法,多線程,新生代)

(3):Parallel Scavenge 收集器(多線程,復(fù)制算法,新生代,高吞吐量)

(4):Serial Old 收集器(標(biāo)記-整理算法,老年代)

(5):Parallel Old 收集器(標(biāo)記-整理算法,老年代,注重吞吐量的場(chǎng)景下,jdk8默認(rèn)采用 Parallel Scavenge + Parallel Old 的組合)

(6):CMS 收集器(標(biāo)記-清除算法,老年代,垃圾回收線程幾乎能做到與用戶線程同時(shí)工作,吞吐量低,內(nèi)存碎片)以犧牲吞吐量為代價(jià)來(lái)獲得最短回收停頓時(shí)間-XX:+UseConcMarkSweepGCjdk1.8 默認(rèn)垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)jdk1.9 默認(rèn)垃圾收集器G1

使用場(chǎng)景:

(1):應(yīng)用程序?qū)νnD比較敏感

(2):在JVM中,有相對(duì)較多存活時(shí)間較長(zhǎng)的對(duì)象(老年代比較大)會(huì)更適合使用CMS

cms垃圾回收過(guò)程:

(1):初始標(biāo)識(shí)<找到gcroot(stw)>

GC Roots有以下幾種:

1:系統(tǒng)類(lèi)加載器加載的對(duì)象

2:處于激活狀態(tài)的線程

3:JNI棧中的對(duì)象

4:正在被用于同步的各種鎖對(duì)象

5:JVM自身持有的對(duì)象,比如系統(tǒng)類(lèi)加載器等。

(2):并發(fā)標(biāo)記(三色標(biāo)記算法)三色標(biāo)記算法處理并發(fā)標(biāo)記出現(xiàn)對(duì)象引用變化情況:黑:自己+子對(duì)象標(biāo)記完成灰:自己完成,子對(duì)象未完成白:未標(biāo)記;并發(fā)標(biāo)記 黑->灰->白重新標(biāo)記 灰->白引用消失,黑引用指向->白,導(dǎo)致白漏標(biāo)cms處理辦法是incremental update方案 (增量更新)把黑色變成灰色多線程下并發(fā)標(biāo)記依舊會(huì)產(chǎn)生漏標(biāo)問(wèn)題,所以cms必須remark一遍(jdk1.9以后不用cms了)

G1 處理方案:

SATB(snapshot at the begining)把白放入棧中,標(biāo)記過(guò)程是和應(yīng)用程序并發(fā)運(yùn)行的(不需要Stop-The-World)這種方式會(huì)造成某些是垃圾的對(duì)象也被當(dāng)做是存活的,所以G1會(huì)使得占用的內(nèi)存被實(shí)際需要的內(nèi)存大。不過(guò)下一次就回收了ZGC 處理方案:顏色指針(color pointers) 2*42方=4T

(3):重新標(biāo)記(stw)

(4)并發(fā)清理

備注:重新標(biāo)記是防止標(biāo)記成垃圾之后,對(duì)象被引用

(5):G1 收集器(新生代 + 老年代,在多 CPU 和大內(nèi)存的場(chǎng)景下有很好的性能)G1在java9 便是默認(rèn)的垃圾收集器,是cms 的替代者邏輯分代,用分區(qū)(region)的思想(默認(rèn)分2048份) 還是有stw為解決CMS算法產(chǎn)生空間碎片HotSpot提供垃圾收集器,通過(guò)-XX:+UseG1GC來(lái)啟用

G1中提供了三種模式垃圾回收模式

(1):young gc(eden region被耗盡無(wú)法申請(qǐng)內(nèi)存時(shí),就會(huì)觸發(fā))

(2):mixed gc(當(dāng)老年代大小占整個(gè)堆大小百分比達(dá)到該閾值時(shí),會(huì)觸發(fā))

(3):full gc(對(duì)象內(nèi)存分配速度過(guò)快,mixed gc來(lái)不及回收,導(dǎo)致老年代被填滿,就會(huì)觸發(fā))

(8):ZGC和shenandoah (oracle產(chǎn)收費(fèi)) no stw

arthas 監(jiān)控工具

(1):dashboard命令查看總體jvm運(yùn)行情況

(2):jvm顯示jvm詳細(xì)信息

(3):thread 顯示jvm里面所有線程信息(類(lèi)似于jstack)  查看死鎖線程命令thread -b

(4):sc * 顯示所有類(lèi)(search class)

(5):trace 跟蹤方法

定位頻繁full GC,堆內(nèi)存滿 oom

第一步:jps獲取進(jìn)程號(hào)第二步:jmap -histo pid | head -20 得知有個(gè)對(duì)象在不斷創(chuàng)建備注:jmap如果線上服務(wù)器堆內(nèi)存特別大,,會(huì)卡死需堆轉(zhuǎn)存(一般會(huì)說(shuō)在測(cè)試環(huán)境壓測(cè),導(dǎo)出轉(zhuǎn)存)-XX:+HeapDumpOnOutOfMemoryError或jmap -dumpLformat=b,file=xxx pid 轉(zhuǎn)出文件進(jìn)行分析(arthas沒(méi)有實(shí)現(xiàn)jmap命令)heapdump --live /xxx/xx.hprof導(dǎo)出文件

G1垃圾回收器(重點(diǎn))

回收過(guò)程(1):young gc(年輕代回收)--當(dāng)年輕代的Eden區(qū)用盡時(shí)--stw第一階段,掃描根。根是指static變量指向的對(duì)象,正在執(zhí)行的方法調(diào)用鏈條上的局部變量等第二階段,更新RS(Remembered Sets)。處理dirty card queue中的card,更新RS。此階段完成后,RS可以準(zhǔn)確的反映老年代對(duì)所在的內(nèi)存分段中對(duì)象的引用第三階段,處理RS。識(shí)別被老年代對(duì)象指向的Eden中的對(duì)象,這些被指向的Eden中的對(duì)象被認(rèn)為是存活的對(duì)象。第四階段,復(fù)制對(duì)象。此階段,對(duì)象樹(shù)被遍歷,Eden區(qū)內(nèi)存段中存活的對(duì)象會(huì)被復(fù)制到Survivor區(qū)中空的內(nèi)存分段第五階段,處理引用。處理Soft,Weak,Phantom,F(xiàn)inal,JNI Weak 等引用。

(2):concrruent marking(老年代并發(fā)標(biāo)記)當(dāng)堆內(nèi)存使用達(dá)到一定值(默認(rèn)45%)時(shí),不需要Stop-The-World,在并發(fā)標(biāo)記前先進(jìn)行一次young gc

(3):混合回收(mixed gc)并發(fā)標(biāo)記過(guò)程結(jié)束以后,緊跟著就會(huì)開(kāi)始混合回收過(guò)程。混合回收的意思是年輕代和老年代會(huì)同時(shí)被回收

(4):Full GC?Full GC是指上述方式不能正常工作,G1會(huì)停止應(yīng)用程序的執(zhí)行,使用單線程的內(nèi)存回收算法進(jìn)行垃圾回收,性能會(huì)非常差,應(yīng)用程序停頓時(shí)間會(huì)很長(zhǎng)。要避免Full GC的發(fā)生,一旦發(fā)生需要進(jìn)行調(diào)整。

什么時(shí)候發(fā)生Full GC呢?

比如堆內(nèi)存太小,當(dāng)G1在復(fù)制存活對(duì)象的時(shí)候沒(méi)有空的內(nèi)存分段可用,則會(huì)回退到full gc,這種情況可以通過(guò)增大內(nèi)存解決

盡管G1堆內(nèi)存仍然是分代的,但是同一個(gè)代的內(nèi)存不再采用連續(xù)的內(nèi)存結(jié)構(gòu)

年輕代分為Eden和Survivor兩個(gè)區(qū),老年代分為Old和Humongous兩個(gè)區(qū)

新分配的對(duì)象會(huì)被分配到Eden區(qū)的內(nèi)存分段上

Humongous區(qū)用于保存大對(duì)象,如果一個(gè)對(duì)象占用的空間超過(guò)內(nèi)存分段Region的一半;

如果對(duì)象的大小超過(guò)一個(gè)甚至幾個(gè)分段的大小,則對(duì)象會(huì)分配在物理連續(xù)的多個(gè)Humongous分段上。

Humongous對(duì)象因?yàn)檎加脙?nèi)存較大并且連續(xù)會(huì)被優(yōu)先回收

為了在回收單個(gè)內(nèi)存分段的時(shí)候不必對(duì)整個(gè)堆內(nèi)存的對(duì)象進(jìn)行掃描(單個(gè)內(nèi)存分段中的對(duì)象可能被其他內(nèi)存分段中的對(duì)象引用)引入了RS數(shù)據(jù)結(jié)構(gòu)。RS使得G1可以在年輕代回收的時(shí)候不必去掃描老年代的對(duì)象,從而提高了性能。每一個(gè)內(nèi)存分段都對(duì)應(yīng)一個(gè)RS,RS保存了來(lái)自其他分段內(nèi)的對(duì)象對(duì)于此分段的引用

JVM會(huì)對(duì)應(yīng)用程序的每一個(gè)引用賦值語(yǔ)句object.field=object進(jìn)行記錄和處理,把引用關(guān)系更新到RS中。但是這個(gè)RS的更新并不是實(shí)時(shí)的。G1維護(hù)了一個(gè)Dirty Card Queue

那為什么不在引用賦值語(yǔ)句處直接更新RS呢?

這是為了性能的需要,使用隊(duì)列性能會(huì)好很多。

線程本地分配緩沖區(qū)(TLAB:Thread Local Allocation Buffer)?

棧上分配->tlab->堆上分配由于堆內(nèi)存是應(yīng)用程序共享的,應(yīng)用程序的多個(gè)線程在分配內(nèi)存的時(shí)候需要加鎖以進(jìn)行同步。為了避免加鎖,提高性能每一個(gè)應(yīng)用程序的線程會(huì)被分配一個(gè)TLAB。TLAB中的內(nèi)存來(lái)自于G1年輕代中的內(nèi)存分段。當(dāng)對(duì)象不是Humongous對(duì)象,TLAB也能裝的下的時(shí)候,對(duì)象會(huì)被優(yōu)先分配于創(chuàng)建此對(duì)象的線程的TLAB中。這樣分配會(huì)很快,因?yàn)門(mén)LAB隸屬于線程,所以不需要加鎖

PLAB:Promotion Thread Local Allocation Buffer

G1會(huì)在年輕代回收過(guò)程中把Eden區(qū)中的對(duì)象復(fù)制(“提升”)到Survivor區(qū)中,Survivor區(qū)中的對(duì)象復(fù)制到Old區(qū)中。G1的回收過(guò)程是多線程執(zhí)行的,為了避免多個(gè)線程往同一個(gè)內(nèi)存分段進(jìn)行復(fù)制,那么復(fù)制的過(guò)程也需要加鎖。為了避免加鎖,G1的每個(gè)線程都關(guān)聯(lián)了一個(gè)PLAB,這樣就不需要進(jìn)行加鎖了

OOM問(wèn)題定位方法

(1):jmap -heap 10765如上圖,可以查看新生代,老生代堆內(nèi)存的分配大小以及使用情況;

(2):jstat 查看GC收集情況

(3):jmap -dump:live,format=b,file=到本地

(4):通過(guò)MAT工具打開(kāi)分析

DUBBO

dubbo流程

(1):生產(chǎn)者(Provider)啟動(dòng),向注冊(cè)中心(Register)注冊(cè)

(2):消費(fèi)者(Consumer)訂閱,而后注冊(cè)中心通知消費(fèi)者

(3):消費(fèi)者從生產(chǎn)者進(jìn)行消費(fèi)

(4):監(jiān)控中心(Monitor)統(tǒng)計(jì)生產(chǎn)者和消費(fèi)者

Dubbo推薦使用什么序列化框架,還有哪些?

推薦使用Hessian序列化,還有Duddo、FastJson、Java自帶序列化

Dubbo默認(rèn)使用的是什么通信框架,還有哪些?

默認(rèn)使用 Netty 框架,也是推薦的選擇,另外內(nèi)容還集成有Mina、Grizzly。

Dubbo有哪幾種負(fù)載均衡策略,默認(rèn)是哪種?

(1):隨機(jī)調(diào)用<默認(rèn)>

(2):權(quán)重輪詢

(3):最少活躍數(shù)

(4):一致性Hash

RPC流程

(1)消費(fèi)者調(diào)用需要消費(fèi)的服務(wù),

(2):客戶端存根將方法、入?yún)⒌刃畔⑿蛄谢l(fā)送給服務(wù)端存根

(3):服務(wù)端存根反序列化操作根據(jù)解碼結(jié)果調(diào)用本地的服務(wù)進(jìn)行相關(guān)處理

(4):本地服務(wù)執(zhí)行具體業(yè)務(wù)邏輯并將處理結(jié)果返回給服務(wù)端存根

(5):服務(wù)端存根序列化

(6):客戶端存根反序列化

(7):服務(wù)消費(fèi)方得到最終結(jié)果

RPC框架的實(shí)現(xiàn)目標(biāo)PC框架的實(shí)現(xiàn)目標(biāo)是把調(diào)用、編碼/解碼的過(guò)程給封裝起來(lái),讓用戶感覺(jué)上像調(diào)用本地服務(wù)一樣的調(diào)用遠(yuǎn)程服務(wù)

服務(wù)暴露、服務(wù)引用、服務(wù)調(diào)用(TODO)

Redis

redis單線程為什么執(zhí)行速度這么快?

(1):純內(nèi)存操作,避免大量訪問(wèn)數(shù)據(jù)庫(kù),減少直接讀取磁盤(pán)數(shù)據(jù),redis將數(shù)據(jù)儲(chǔ)存在內(nèi)存里面,讀寫(xiě)數(shù)據(jù)的時(shí)候都不會(huì)受到硬盤(pán) I/O 速度的限制,所以速度快

(2):?jiǎn)尉€程操作,避免了不必要的上下文切換和競(jìng)爭(zhēng)條件,也不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗CPU,不用去考慮各種鎖的問(wèn)題,不存在加鎖釋放鎖操作,沒(méi)有因?yàn)榭赡艹霈F(xiàn)死鎖而導(dǎo)致的性能消耗

(3):采用了非阻塞I/O多路復(fù)用機(jī)制

Redis數(shù)據(jù)結(jié)構(gòu)底層實(shí)現(xiàn)

String:

(1)Simple dynamic string(SDS)的數(shù)據(jù)結(jié)構(gòu)

struct sdshdr{
 //記錄buf數(shù)組中已使用字節(jié)的數(shù)量
 //等于 SDS 保存字符串的長(zhǎng)度
 int len;
 //記錄 buf 數(shù)組中未使用字節(jié)的數(shù)量
 int free;
 //字節(jié)數(shù)組,用于保存字符串
 char buf[];
}

它的優(yōu)點(diǎn):(1)不會(huì)出現(xiàn)字符串變更造成的內(nèi)存溢出問(wèn)題

(2)獲取字符串長(zhǎng)度時(shí)間復(fù)雜度為1

(3)空間預(yù)分配, 惰性空間釋放free字段,會(huì)默認(rèn)留夠一定的空間防止多次重分配內(nèi)存

應(yīng)用場(chǎng)景:String 緩存結(jié)構(gòu)體用戶信息,計(jì)數(shù)

Hash:

數(shù)組+鏈表的基礎(chǔ)上,進(jìn)行了一些rehash優(yōu)化;1.Reids的Hash采用鏈地址法來(lái)處理沖突,然后它沒(méi)有使用紅黑樹(shù)優(yōu)化。

2.哈希表節(jié)點(diǎn)采用單鏈表結(jié)構(gòu)。

3.rehash優(yōu)化 (采用分而治之的思想,將龐大的遷移工作量劃分到每一次CURD中,避免了服務(wù)繁忙)

應(yīng)用場(chǎng)景:保存結(jié)構(gòu)體信息可部分獲取不用序列化所有字段

List:

應(yīng)用場(chǎng)景:(1):比如twitter的關(guān)注列表,粉絲列表等都可以用Redis的list結(jié)構(gòu)來(lái)實(shí)現(xiàn)

(2):list的實(shí)現(xiàn)為一個(gè)雙向鏈表,即可以支持反向查找和遍歷

Set:

內(nèi)部實(shí)現(xiàn)是一個(gè) value為null的HashMap,實(shí)際就是通過(guò)計(jì)算hash的方式來(lái)快速排重的,這也是set能提供判斷一個(gè)成員 是否在集合內(nèi)的原因。應(yīng)用場(chǎng)景:去重的場(chǎng)景,交集(sinter)、并集(sunion)、差集(sdiff),實(shí)現(xiàn)如共同關(guān)注、共同喜好、二度好友等功能

Zset:

內(nèi)部使用HashMap和跳躍表(SkipList)來(lái)保證數(shù)據(jù)的存儲(chǔ)和有序,HashMap里放的是成員到score的映射,而跳躍表里存放的是所有的成員,排序依據(jù)是HashMap里存的score,使用跳躍表的結(jié)構(gòu)可以獲得比較高的查找效率,并且在實(shí)現(xiàn)上比較簡(jiǎn)單。跳表:每個(gè)節(jié)點(diǎn)中維持多個(gè)指向其他節(jié)點(diǎn)的指針,從而達(dá)到快速訪問(wèn)節(jié)點(diǎn)的目的應(yīng)用場(chǎng)景:實(shí)現(xiàn)延時(shí)隊(duì)列

redis事務(wù)

(1):Multi開(kāi)啟事務(wù)

(2):Exec執(zhí)行事務(wù)塊內(nèi)命令

(3):Discard 取消事務(wù)

(4):Watch 監(jiān)視一個(gè)或多個(gè)key,如果事務(wù)執(zhí)行前key被改動(dòng),事務(wù)將打斷

redis事務(wù)的實(shí)現(xiàn)特征

(1):所有命令都將會(huì)被串行化的順序執(zhí)行,事務(wù)執(zhí)行期間,Redis不會(huì)再為其它客戶端的請(qǐng)求提供任何服務(wù),從而保證了事物中的所有命令被原子的執(zhí)行

(2):Redis事務(wù)中如果有某一條命令執(zhí)行失敗,其后的命令仍然會(huì)被繼續(xù)執(zhí)行

(3):在事務(wù)開(kāi)啟之前,如果客戶端與服務(wù)器之間出現(xiàn)通訊故障并導(dǎo)致網(wǎng)絡(luò)斷開(kāi),其后所有待執(zhí)行的語(yǔ)句都將不會(huì)被服務(wù)器執(zhí)行。然而如果網(wǎng)絡(luò)中斷事件是發(fā)生在客戶端執(zhí)行EXEC命令之后,那么該事務(wù)中的所有命令都會(huì)被服務(wù)器執(zhí)行

(4):當(dāng)使用Append-Only模式時(shí),Redis會(huì)通過(guò)調(diào)用系統(tǒng)函數(shù)write將該事務(wù)內(nèi)的所有寫(xiě)操作在本次調(diào)用中全部寫(xiě)入磁盤(pán)。

然而如果在寫(xiě)入的過(guò)程中出現(xiàn)系統(tǒng)崩潰,如電源故障導(dǎo)致的宕機(jī),那么此時(shí)也許只有部分?jǐn)?shù)據(jù)被寫(xiě)入到磁盤(pán),而另外一部分?jǐn)?shù)據(jù)卻已經(jīng)丟失。

Redis服務(wù)器會(huì)在重新啟動(dòng)時(shí)執(zhí)行一系列必要的一致性檢測(cè),一旦發(fā)現(xiàn)類(lèi)似問(wèn)題,就會(huì)立即退出并給出相應(yīng)的錯(cuò)誤提示。此時(shí),我們就要充分利用Redis工具包中提供的redis-check-aof工具,該工具可以幫助我們定位到數(shù)據(jù)不一致的錯(cuò)誤,并將已經(jīng)寫(xiě)入的部分?jǐn)?shù)據(jù)進(jìn)行回滾。修復(fù)之后我們就可以再次重新啟動(dòng)Redis服務(wù)器了

Redis的同步機(jī)制?

(1):全量拷貝,1.slave第一次啟動(dòng)時(shí),連接Master,發(fā)送PSYNC命令,

2.master會(huì)執(zhí)行bgsave命令來(lái)生成rdb文件,期間的所有寫(xiě)命令將被寫(xiě)入緩沖區(qū)。

  1. master bgsave執(zhí)行完畢,向slave發(fā)送rdb文件

  2. slave收到rdb文件,丟棄所有舊數(shù)據(jù),開(kāi)始載入rdb文件

  3. rdb文件同步結(jié)束之后,slave執(zhí)行從master緩沖區(qū)發(fā)送過(guò)來(lái)的所以寫(xiě)命令。

  4. 此后 master 每執(zhí)行一個(gè)寫(xiě)命令,就向slave發(fā)送相同的寫(xiě)命令。

    (2):增量拷貝如果出現(xiàn)網(wǎng)絡(luò)閃斷或者命令丟失等異常情況,從節(jié)點(diǎn)之前保存了自身已復(fù)制的偏移量和主節(jié)點(diǎn)的運(yùn)行ID

  5. 主節(jié)點(diǎn)根據(jù)偏移量把復(fù)制積壓緩沖區(qū)里的數(shù)據(jù)發(fā)送給從節(jié)點(diǎn),保證主從復(fù)制進(jìn)入正常狀態(tài)。

    redis集群模式性能優(yōu)化

    (1) Master最好不要做任何持久化工作,如RDB內(nèi)存快照和AOF日志文件

    (2) 如果數(shù)據(jù)比較重要,某個(gè)Slave開(kāi)啟AOF備份數(shù)據(jù),策略設(shè)置為每秒同步一次

    (3) 為了主從復(fù)制的速度和連接的穩(wěn)定性,Master和Slave最好在同一個(gè)局域網(wǎng)內(nèi)

    (4) 盡量避免在壓力很大的主庫(kù)上增加從庫(kù)

    (5) 主從復(fù)制不要用圖狀結(jié)構(gòu),用單向鏈表結(jié)構(gòu)更為穩(wěn)定,即:Master <- Slave1 <- Slave2 <- Slave3…這樣的結(jié)構(gòu)方便解決單點(diǎn)故障問(wèn)題,實(shí)現(xiàn)Slave對(duì)Master的替換。如果Master掛了,可以立刻啟用Slave1做Master,其他不變。

    Redis集群方案

    (1):官方cluster方案

    (2):twemproxy

    代理方案twemproxy是一個(gè)單點(diǎn),很容易對(duì)其造成很大的壓力,所以通常會(huì)結(jié)合keepalived來(lái)實(shí)twemproy的高可用

    (3):codis基于客戶端來(lái)進(jìn)行分片

集群不可用場(chǎng)景

(1):master掛掉,且當(dāng)前master沒(méi)有slave

(2):集群超過(guò)半數(shù)以上master掛掉,無(wú)論是否有slave集群進(jìn)入fail狀態(tài)

redis 最適合的場(chǎng)景

(1):會(huì)話緩存session cache

(2):排行榜/計(jì)數(shù)器ZRANGE

(3):發(fā)布/訂閱

緩存淘汰策略

(1):先進(jìn)先出算法(FIFO)

(2):最近使用最少Least Frequently Used(LFU)

(3):最長(zhǎng)時(shí)間未被使用的Least Recently Used(LRU)

當(dāng)存在熱點(diǎn)數(shù)據(jù)時(shí),LRU的效率很好,但偶發(fā)性的、周期性的批量操作會(huì)導(dǎo)致LRU命中率急劇下降,緩存污染情況比較嚴(yán)重

redis過(guò)期key刪除策略

(1):惰性刪除,cpu友好,但是浪費(fèi)cpu資源

(2):定時(shí)刪除(不常用)

(3):定期刪除,cpu友好,節(jié)省空間

緩存雪崩以及處理辦法

同一時(shí)刻大量緩存失效;

處理方法:

(1):緩存數(shù)據(jù)增加過(guò)期標(biāo)記

(2):設(shè)置不同的緩存失效時(shí)間

(3):雙層緩存策略C1為短期,C2為長(zhǎng)期

(4):定時(shí)更新策略

緩存擊穿原因以及處理辦法

頻繁請(qǐng)求查詢系統(tǒng)中不存在的數(shù)據(jù)導(dǎo)致;

處理方法:

(1):cache null策略,查詢反饋結(jié)果為null仍然緩存這個(gè)null結(jié)果,設(shè)置不超過(guò)5分鐘過(guò)期時(shí)間

(2):布隆過(guò)濾器,所有可能存在的數(shù)據(jù)映射到足夠大的bitmap中g(shù)oogle布隆過(guò)濾器:基于內(nèi)存,重啟失效不支持大數(shù)據(jù)量,無(wú)法在分布式場(chǎng)景redis布隆過(guò)濾器:可擴(kuò)展性,不存在重啟失效問(wèn)題,需要網(wǎng)絡(luò)io,性能低于google

redis阻塞原因

(1):數(shù)據(jù)結(jié)構(gòu)使用不合理bigkey

(2):CPU飽和

(3):持久化阻塞,rdb fork子線程,aof每秒刷盤(pán)等

hot key出現(xiàn)造成集群訪問(wèn)量?jī)A斜解決辦法

(1):使用本地緩存

(2):利用分片算法的特性,對(duì)key進(jìn)行打散處理(給hot key加上前綴或者后綴,把一個(gè)hotkey 的數(shù)量變成 redis 實(shí)例個(gè)數(shù)N的倍數(shù)M,從而由訪問(wèn)一個(gè) redis key 變成訪問(wèn) N * M 個(gè)redis key)

Redis分布式鎖

2.6版本以后lua腳本保證setnx跟setex進(jìn)行原子性(setnx之后,未setex,服務(wù)掛了,鎖不釋放)a獲取鎖,超過(guò)過(guò)期時(shí)間,自動(dòng)釋放鎖,b獲取到鎖執(zhí)行,a代碼執(zhí)行完remove鎖,a和b是一樣的key,導(dǎo)致a釋放了b的鎖。解決辦法:remove之前判斷value(高并發(fā)下value可能被修改,應(yīng)該用lua來(lái)保證原子性)

Redis如何做持久化

bgsave做鏡像全量持久化,aof做增量持久化。因?yàn)閎gsave會(huì)耗費(fèi)較長(zhǎng)時(shí)間,不夠?qū)崟r(shí),在停機(jī)的時(shí)候會(huì)導(dǎo)致大量丟失數(shù)據(jù) ,所以需要aof來(lái)配合使用。在redis實(shí)例重啟時(shí),會(huì)使用bgsave持久化文件重新構(gòu)建內(nèi)存,再使用aof重放近期的操作指令來(lái) 實(shí) 現(xiàn)完整恢復(fù)重啟之前的狀態(tài)。

對(duì)方追問(wèn)那如果突然機(jī)器掉電會(huì)怎樣?

取決于aof日志sync屬性的配置,如果不要求性能,在每條寫(xiě)指令時(shí)都sync一下磁盤(pán),就不會(huì)丟失數(shù)據(jù)。但是在高性能的要求下每次都sync是不現(xiàn)實(shí)的,一般都使用定時(shí)sync,比如1s1次,這個(gè)時(shí)候最多就會(huì)丟失1s的數(shù)據(jù).

redis鎖續(xù)租問(wèn)題?

(1):基于redis的redission分布式可重入鎖RLock,以及配合java集合中l(wèi)ock;

(2):Redission 內(nèi)部提供了一個(gè)監(jiān)控鎖的看門(mén)狗,不斷延長(zhǎng)鎖的有效期,默認(rèn)檢查鎖的超時(shí)時(shí)間是30秒

(3):此方案的問(wèn)題:如果你對(duì)某個(gè)redis master實(shí)例,寫(xiě)入了myLock這種鎖key的value,此時(shí)會(huì)異步復(fù)制給對(duì)應(yīng)的master ,slave實(shí)例。但是這個(gè)過(guò)程中一旦發(fā)生redis master宕機(jī),主備切換,redis slave變?yōu)榱藃edis master。

接著就會(huì)導(dǎo)致,客戶端2來(lái)嘗試加鎖的時(shí)候,在新的redis master上完成了加鎖,而客戶端1也以為自己成功加了鎖。此時(shí)就會(huì)導(dǎo)致多個(gè)客戶端對(duì)一個(gè)分布式鎖完成了加鎖解決辦法:只需要將新的redis實(shí)例,在一個(gè)TTL時(shí)間內(nèi),對(duì)客戶端不可用即可,在這個(gè)時(shí)間內(nèi),所有客戶端鎖將被失效或者自動(dòng)釋放.

bgsave的原理是什么?

fork和cow。fork是指redis通過(guò)創(chuàng)建子進(jìn)程來(lái)進(jìn)行bgsave操作,cow指的是copy on write,子進(jìn)程創(chuàng)建后,父子進(jìn)程共享數(shù)據(jù)段,父進(jìn)程繼續(xù)提供讀寫(xiě)服務(wù),寫(xiě)進(jìn)的頁(yè)面數(shù)據(jù)會(huì)逐漸和子進(jìn)程分離開(kāi)來(lái)。

RDB與AOF區(qū)別

(1):R文件格式緊湊,方便數(shù)據(jù)恢復(fù),保存rdb文件時(shí)父進(jìn)程會(huì)fork出子進(jìn)程由其完成具體持久化工作,最大化redis性能,恢復(fù)大數(shù)據(jù)集速度更快,只有手動(dòng)提交save命令或關(guān)閉命令時(shí)才觸發(fā)備份操作;

(2):A記錄對(duì)服務(wù)器的每次寫(xiě)操作(默認(rèn)1s寫(xiě)入一次),保存數(shù)據(jù)更完整,在redis重啟是會(huì)重放這些命令來(lái)恢復(fù)數(shù)據(jù),操作效率高,故障丟失數(shù)據(jù)更少,但是文件體積更大;

1億個(gè)key,其中有10w個(gè)key是以某個(gè)固定的已知的前綴開(kāi)頭的,如果將它們?nèi)空页鰜?lái)?

使用keys指令可以掃出指定模式的key列表。如果這個(gè)redis正在給線上的業(yè)務(wù)提供服務(wù),那使用keys指令會(huì)有什么問(wèn)題?redis的單線程的。keys指令會(huì)導(dǎo)致線程阻塞一段時(shí)間,線上服務(wù)會(huì)停頓,直到指令執(zhí)行完畢,服務(wù)才能恢復(fù)。這個(gè)時(shí)候可以使用scan指令,scan指令可以無(wú)阻塞的提取出指定模式的key列表,但是會(huì)有一定的重復(fù)概率,在客戶端做一次去重就可以了 ,但是整體所花費(fèi)的時(shí)間會(huì)比直接用keys指令長(zhǎng)。

如何使用Redis做異步隊(duì)列?

一般使用list結(jié)構(gòu)作為隊(duì)列,rpush生產(chǎn)消息,lpop消費(fèi)消息。當(dāng)lpop沒(méi)有消息的時(shí)候,要適當(dāng)sleep一會(huì)再重試。

可不可以不用sleep呢?

list還有個(gè)指令叫blpop,在沒(méi)有消息的時(shí)候,它會(huì)阻塞住直到消息到來(lái)。

能不能生產(chǎn)一次消費(fèi)多次呢?

使用pub/sub主題訂閱者模式,可以實(shí)現(xiàn)1:N的消息隊(duì)列。

pub/sub有什么缺點(diǎn)?

在消費(fèi)者下線的情況下,生產(chǎn)的消息會(huì)丟失,得使用專(zhuān)業(yè)的消息隊(duì)列如rabbitmq等。

redis如何實(shí)現(xiàn)延時(shí)隊(duì)列?

使用sortedset,想要執(zhí)行時(shí)間的時(shí)間戳作為score,消息內(nèi)容作為key調(diào)用zadd來(lái)生產(chǎn)消息,消費(fèi)者用zrangebyscore指令獲取N秒之前的數(shù)據(jù)輪詢進(jìn)行處理。

為啥redis zset使用跳躍鏈表而不用紅黑樹(shù)實(shí)現(xiàn)?

(1):skiplist的復(fù)雜度和紅黑樹(shù)一樣,而且實(shí)現(xiàn)起來(lái)更簡(jiǎn)單。

(2):在并發(fā)環(huán)境下紅黑樹(shù)在插入和刪除時(shí)需要rebalance,性能不如跳表。

MYSQL

數(shù)據(jù)庫(kù)三范式

一:確保每列的原子性

二:非主鍵列不存在對(duì)主鍵的部分依賴(lài) (要求每個(gè)表只描述一件事情)

三:滿足第二范式,并且表中的列不存在對(duì)非主鍵列的傳遞依賴(lài)

數(shù)據(jù)庫(kù)主從復(fù)制原理

(1):主庫(kù)db的更新事件(update、insert、delete)被寫(xiě)到binlog

(2):主庫(kù)創(chuàng)建一個(gè)binlog dump thread線程,把binlog的內(nèi)容發(fā)送到從庫(kù)

(3):從庫(kù)創(chuàng)建一個(gè)I/O線程,讀取主庫(kù)傳過(guò)來(lái)的binlog內(nèi)容并寫(xiě)入到relay log.

(4):從庫(kù)還會(huì)創(chuàng)建一個(gè)SQL線程,從relay log里面讀取內(nèi)容寫(xiě)入到slave的db.

復(fù)制方式分類(lèi)

(1):異步復(fù)制(默認(rèn))主庫(kù)寫(xiě)入binlog日志后即可成功返回客戶端,無(wú)須等待binlog日志傳遞給從庫(kù)的過(guò)程,但是一旦主庫(kù)宕機(jī),就有可能出現(xiàn)丟失數(shù)據(jù)的情況。

(2)半同步復(fù)制:( 5.5版本之后)(安裝半同步復(fù)制插件)確保從庫(kù)接收完成主庫(kù)傳遞過(guò)來(lái)的binlog內(nèi)容已經(jīng)寫(xiě)入到自己的relay log(傳送log)后才會(huì)通知主庫(kù)上面的等待線程。如果等待超時(shí),則關(guān)閉半同步復(fù)制,并自動(dòng)轉(zhuǎn)換為異步復(fù)制模式,直到至少有一臺(tái)從庫(kù)通知主庫(kù)已經(jīng)接收到binlog信息為止

存儲(chǔ)引擎

(1):Myiasm是mysql默認(rèn)的存儲(chǔ)引擎,不支持?jǐn)?shù)據(jù)庫(kù)事務(wù),行級(jí)鎖,外鍵;插入更新需鎖表,效率低,查詢速度快,Myisam使用的是非聚集索引

(2):innodb 支持事務(wù),底層為B+樹(shù)實(shí)現(xiàn),適合處理多重并發(fā)更新操作,普通select都是快照讀,快照讀不加鎖。InnoDb使用的是聚集索引

聚集索引

(1):聚集索引就是以主鍵創(chuàng)建的索引

(2):每個(gè)表只能有一個(gè)聚簇索引,因?yàn)橐粋€(gè)表中的記錄只能以一種物理順序存放,實(shí)際的數(shù)據(jù)頁(yè)只能按照一顆 B+ 樹(shù)進(jìn)行排序

(3):表記錄的排列順序和與索引的排列順序一致

(4):聚集索引存儲(chǔ)記錄是物理上連續(xù)存在

(5):聚簇索引主鍵的插入速度要比非聚簇索引主鍵的插入速度慢很多

(6):聚簇索引適合排序,非聚簇索引不適合用在排序的場(chǎng)合,因?yàn)榫鄞厮饕~節(jié)點(diǎn)本身就是索引和數(shù)據(jù)按相同順序放置在一起,索引序即是數(shù)據(jù)序,數(shù)據(jù)序即是索引序,所以很快。非聚簇索引葉節(jié)點(diǎn)是保留了一個(gè)指向數(shù)據(jù)的指針,索引本身當(dāng)然是排序的,但是數(shù)據(jù)并未排序,數(shù)據(jù)查詢的時(shí)候需要消耗額外更多的I/O,所以較慢

(7):更新聚集索引列的代價(jià)很高,因?yàn)闀?huì)強(qiáng)制innodb將每個(gè)被更新的行移動(dòng)到新的位置

非聚集索引

(1):除了主鍵以外的索引

(2):聚集索引的葉節(jié)點(diǎn)就是數(shù)據(jù)節(jié)點(diǎn),而非聚簇索引的葉節(jié)點(diǎn)仍然是索引節(jié)點(diǎn),并保留一個(gè)鏈接指向?qū)?yīng)數(shù)據(jù)塊

(3):聚簇索引適合排序,非聚簇索引不適合用在排序的場(chǎng)合

(4):聚集索引存儲(chǔ)記錄是物理上連續(xù)存在,非聚集索引是邏輯上的連續(xù)。

使用聚集索引為什么查詢速度會(huì)變快?

使用聚簇索引找到包含第一個(gè)值的行后,便可以確保包含后續(xù)索引值的行在物理相鄰

建立聚集索引有什么需要注意的地方嗎?

在聚簇索引中不要包含經(jīng)常修改的列,因?yàn)榇a值修改后,數(shù)據(jù)行必須移動(dòng)到新的位置,索引此時(shí)會(huì)重排,會(huì)造成很大的資源浪費(fèi)

InnoDB 表對(duì)主鍵生成策略是什么樣的?

優(yōu)先使用用戶自定義主鍵作為主鍵,如果用戶沒(méi)有定義主鍵,則選取一個(gè)Unique鍵作為主鍵,如果表中連Unique鍵都沒(méi)有定義的話,則InnoDB會(huì)為表默認(rèn)添加一個(gè)名為row_id隱藏列作為主鍵。

非聚集索引最多可以有多少個(gè)?

每個(gè)表你最多可以建立249個(gè)非聚簇索引。非聚簇索引需要大量的硬盤(pán)空間和內(nèi)存

BTree 與 Hash 索引有什么區(qū)別?

(1):BTree索引可能需要多次運(yùn)用折半查找來(lái)找到對(duì)應(yīng)的數(shù)據(jù)塊(2):HASH索引是通過(guò)HASH函數(shù),計(jì)算出HASH值,在表中找出對(duì)應(yīng)的數(shù)據(jù)(3):大量不同數(shù)據(jù)等值精確查詢,HASH索引效率通常比B+TREE高(4):HASH索引不支持模糊查詢、范圍查詢和聯(lián)合索引中的最左匹配規(guī)則,而這些Btree索引都支持

數(shù)據(jù)庫(kù)索引優(yōu)缺點(diǎn)

(1):需要查詢,排序,分組和聯(lián)合操作的字段適合建立索引

(2):索引多,數(shù)據(jù)更新表越慢,盡量使用字段值不重復(fù)比例大的字段作為索引,聯(lián)合索引比多個(gè)獨(dú)立索引效率高

(3):對(duì)數(shù)據(jù)進(jìn)行頻繁查詢進(jìn)建立索引,如果要頻繁更改數(shù)據(jù)不建議使用索引

(4):當(dāng)對(duì)表中的數(shù)據(jù)進(jìn)行增加、刪除和修改的時(shí)候,索引也要?jiǎng)討B(tài)的維護(hù),降低了數(shù)據(jù)的維護(hù)速度。

索引的底層實(shí)現(xiàn)是B+樹(shù),為何不采用紅黑樹(shù),B樹(shù)?

(1):B+Tree非葉子節(jié)點(diǎn)只存儲(chǔ)鍵值信息,降低B+Tree的高度,所有葉子節(jié)點(diǎn)之間都有一個(gè)鏈指針,數(shù)據(jù)記錄都存放在葉子節(jié)點(diǎn)中

(2):紅黑樹(shù)這種結(jié)構(gòu),h明顯要深的多,效率明顯比B-Tree差很多

(3):B+樹(shù)也存在劣勢(shì),由于鍵會(huì)重復(fù)出現(xiàn),因此會(huì)占用更多的空間。但是與帶來(lái)的性能優(yōu)勢(shì)相比,空間劣勢(shì)往往可以接受,因此B+樹(shù)的在數(shù)據(jù)庫(kù)中的使用比B樹(shù)更加廣泛

索引失效條件

(1):條件是or,如果還想讓or條件生效,給or每個(gè)字段加個(gè)索引

(2):like開(kāi)頭%

(3):如果列類(lèi)型是字符串,那一定要在條件中將數(shù)據(jù)使用引號(hào)引用起來(lái),否則不會(huì)使用索引

(4):where中索引列使用了函數(shù)或有運(yùn)算

數(shù)據(jù)庫(kù)事務(wù)特點(diǎn)

ACID 原子性,一致性,隔離性,永久性

數(shù)據(jù)庫(kù)事務(wù)說(shuō)是如何實(shí)現(xiàn)的?

(1):通過(guò)預(yù)寫(xiě)日志方式實(shí)現(xiàn)的,redo和undo機(jī)制是數(shù)據(jù)庫(kù)實(shí)現(xiàn)事務(wù)的基礎(chǔ)

(2):redo日志用來(lái)在斷電/數(shù)據(jù)庫(kù)崩潰等狀況發(fā)生時(shí)重演一次刷數(shù)據(jù)的過(guò)程,把redo日志里的數(shù)據(jù)刷到數(shù)據(jù)庫(kù)里,保證了事務(wù) 的持久性(Durability)

(3):undo日志是在事務(wù)執(zhí)行失敗的時(shí)候撤銷(xiāo)對(duì)數(shù)據(jù)庫(kù)的操作,保證了事務(wù)的原子性

數(shù)據(jù)庫(kù)事務(wù)隔離級(jí)別

(1):讀未提交read-uncommitted-- 臟,不可重復(fù)讀--幻讀A讀取了B未提交的事務(wù),B回滾,A 出現(xiàn)臟讀;

(2):不可重復(fù)讀read-committed-- 不可重復(fù)讀--幻讀A只能讀B已提交的事務(wù),但是A還沒(méi)結(jié)束,B又更新數(shù)據(jù)隱式提交,然后A又讀了一次出現(xiàn)不可重復(fù)讀;

(3):可重復(fù)讀repeatable-read<默認(rèn)>-- 幻讀事務(wù)開(kāi)啟,不允許其他事務(wù)的UPDATE修改操作A讀取B已提交的事務(wù),然而B(niǎo)在該表插入新的行,之后A在讀取的時(shí)候多出一行,出現(xiàn)幻讀;

(4):串行化serializable--

七種事務(wù)傳播行為

(1)Propagation.REQUIRED<默認(rèn)>如果當(dāng)前存在事務(wù),則加入該事務(wù),如果當(dāng)前不存在事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。

(2)Propagation.SUPPORTS如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前不存在事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。

(3)Propagation.MANDATORY如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前不存在事務(wù),則拋出異常。

(4)Propagation.REQUIRES_NEW重新創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),延緩當(dāng)前的事務(wù)。

(5)Propagation.NOT_SUPPORTED以非事務(wù)的方式運(yùn)行,如果當(dāng)前存在事務(wù),暫停當(dāng)前的事務(wù)。

(6)Propagation.NEVER以非事務(wù)的方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。

(7)Propagation.NESTED如果沒(méi)有,就新建一個(gè)事務(wù);如果有,就在當(dāng)前事務(wù)中嵌套其他事務(wù)。

產(chǎn)生死鎖的四個(gè)必要條件

(1):互斥:資源x的任意一個(gè)時(shí)刻只能被一個(gè)線程持有(2):占有且等待:線程1占有資源x的同時(shí)等待資源y,并不釋放x(3):不可搶占:資源x一旦被線程1占有,其他線程不能搶占x(4):循環(huán)等待:線程1持有x,等待y,線程2持有y,等待x當(dāng)全部滿足時(shí)才會(huì)死鎖

@Transaction

底層實(shí)現(xiàn)是AOP,動(dòng)態(tài)代理(1):實(shí)現(xiàn)是通過(guò)Spring代理來(lái)實(shí)現(xiàn)的。生成當(dāng)前類(lèi)的代理類(lèi),調(diào)用代理類(lèi)的invoke()方法,在invoke()方法中調(diào)用 TransactionInterceptor攔截器的invoke()方法;

(2):非public方式其事務(wù)是失效的;

(3):自調(diào)用也會(huì)失效,因?yàn)閯?dòng)態(tài)代理機(jī)制導(dǎo)致

(4)多個(gè)方法外層加入try...catch,解決辦法是可以在catch里 throw new RuntimeException()來(lái)處理

分布式事務(wù)

XA方案

有一個(gè)事務(wù)管理器的概念,負(fù)責(zé)協(xié)調(diào)多個(gè)數(shù)據(jù)庫(kù)(資源管理器)的事務(wù)不適合高并發(fā)場(chǎng)景,嚴(yán)重依賴(lài)數(shù)據(jù)庫(kù)層面,同步阻塞問(wèn)題;協(xié)調(diào)者故障則所有參與者會(huì)阻塞

TCC方案

嚴(yán)重依賴(lài)代碼補(bǔ)償和回滾,一般銀行用,和錢(qián)相關(guān)的支付、交易等相關(guān)的場(chǎng)景,我們會(huì)用TCCTry,對(duì)各個(gè)服務(wù)的資源做檢測(cè),對(duì)資源進(jìn)行鎖定或者預(yù)留Confirm,在各個(gè)服務(wù)中執(zhí)行實(shí)際的操作Cancel,如果任何一個(gè)服務(wù)的業(yè)務(wù)方法執(zhí)行出錯(cuò),那么這里就需要進(jìn)行補(bǔ)償,即執(zhí)行已操作成功的業(yè)務(wù)邏輯的回滾操作

可靠消息最終一致性方案

1):本地消息服務(wù)本地消息表其實(shí)是國(guó)外的 ebay 搞出來(lái)的這么一套思想。主動(dòng)方是認(rèn)證服務(wù),有個(gè)消息異常處理系統(tǒng),mq,還有消息消費(fèi)端應(yīng)用系統(tǒng),還有采集服務(wù);

  • 在我認(rèn)證返回?cái)?shù)據(jù)中如果有發(fā)票是已經(jīng)認(rèn)證的,在處理認(rèn)證數(shù)據(jù)的操作與發(fā)送消息在同一個(gè)本地事務(wù)中,業(yè)務(wù)執(zhí)行完,消息數(shù)據(jù)也同時(shí)存在一條待確認(rèn)的數(shù)據(jù);
  • 發(fā)送消息給mq,,mq發(fā)送消息給消息消費(fèi)端服務(wù),同時(shí)存一份消息數(shù)據(jù),然后發(fā)送給采集服務(wù),進(jìn)行抵賬表更新操作;
  • 采集服務(wù)邏輯處理完以后反饋給消息消費(fèi)端服務(wù),其服務(wù)刪除消息數(shù)據(jù),同時(shí)通知認(rèn)證服務(wù),把消息記錄改為已確認(rèn)成功費(fèi)狀態(tài);
  • 對(duì)于異常流程,消息異常處理系統(tǒng)會(huì)查詢認(rèn)證服務(wù)中過(guò)期未確認(rèn)的消息發(fā)送給mq,相當(dāng)于重試

2):獨(dú)立消息最終一致性方案:A 主動(dòng)方應(yīng)用系統(tǒng),B消息服務(wù)子系統(tǒng),C消息狀態(tài)確認(rèn)子系統(tǒng),C2消息管理子系統(tǒng)D 消息恢復(fù)子系統(tǒng),mq ,消息消費(fèi)端E ,被動(dòng)系統(tǒng)F

 流程:
A預(yù)發(fā)送消息給B,然后執(zhí)行A業(yè)務(wù)邏輯,B存儲(chǔ)預(yù)發(fā)送消息,A執(zhí)行完業(yè)務(wù)邏輯發(fā)送業(yè)務(wù)操作結(jié)果給B,B更新預(yù)發(fā)送消息為確認(rèn)并發(fā)送消息狀態(tài)同時(shí)發(fā)送消息給mq,然后被E監(jiān)聽(tīng)然后發(fā)送給F消費(fèi)掉
C:對(duì)預(yù)發(fā)送消息異常的處理,去查詢待確認(rèn)狀態(tài)超時(shí)的消息,去A中查詢進(jìn)行數(shù)據(jù)處理,如果A中業(yè)務(wù)處理成功了,那么C需改消息狀態(tài)為確認(rèn)并發(fā)送狀態(tài),然后發(fā)送消息給mq;如果A中業(yè)務(wù)處理失敗了..那么C直接把消息刪除即可.
C2 : 查詢消息的頁(yè)面,對(duì)消息的可視化,以及批量處理死亡消息;
D:B給mq放入數(shù)據(jù)如果失敗,,通過(guò)D去重試,多次重試失敗,消息設(shè)置為死亡 
E:確保F執(zhí)行完成,發(fā)送消息給B刪除消息
優(yōu)化建議: 
 (1)數(shù)據(jù)庫(kù):如果用redis,持久化要配置成appendfsync always,確保每次新添加消息都能持久化進(jìn)磁盤(pán)
 (2)在被動(dòng)方應(yīng)用業(yè)務(wù)冪等性判斷比較麻煩或者比較耗性能情況下,增加消息日志記錄表.用于判斷之前有無(wú)發(fā)送過(guò);

最大努力通知性(定期校對(duì))

(1)業(yè)務(wù)主動(dòng)方完成業(yè)務(wù)處理之后,設(shè)置時(shí)間階梯型通知規(guī)則向業(yè)務(wù)活動(dòng)的被動(dòng)方發(fā)送消息,允許消息丟失.

(2)被動(dòng)方根據(jù)定時(shí)策略,向主動(dòng)方查詢,恢復(fù)丟失的業(yè)務(wù)消息

(3)被動(dòng)方的處理結(jié)果不影響主動(dòng)方的處理結(jié)果

(4)需增加業(yè)務(wù)查詢,通知服務(wù),校對(duì)系統(tǒng)服務(wù)的建設(shè)成本

(5)適用于對(duì)業(yè)務(wù)最終一致性的時(shí)間敏感度低,跨企業(yè)的業(yè)務(wù)通知活動(dòng)

(6)比如銀行通知,商戶通知,交易業(yè)務(wù)平臺(tái)間商戶通知,多次通知,查詢校對(duì)等

Seata(阿里)

應(yīng)用層基于SQL解析實(shí)現(xiàn)了自動(dòng)補(bǔ)償,從而最大程度的降低業(yè)務(wù)侵入性;將分布式事務(wù)中TC(事務(wù)協(xié)調(diào)者)獨(dú)立部署,負(fù)責(zé)事務(wù)的注冊(cè)、回滾;通過(guò)全局鎖實(shí)現(xiàn)了寫(xiě)隔離與讀隔離。

網(wǎng)絡(luò)

TCP和UDP的比較

TCP向上層提供面向連接的可靠服務(wù) ,UDP向上層提供無(wú)連接不可靠服務(wù)。雖然 UDP 并沒(méi)有 TCP 傳輸來(lái)的準(zhǔn)確,但是也能在很多實(shí)時(shí)性要求高的地方有所作為對(duì)數(shù)據(jù)準(zhǔn)確性要求高,速度可以相對(duì)較慢的,可以選用TCP

TCP三次握手

TCP四次揮手

(1):客戶端發(fā)送終止命令FIN

(2):服務(wù)端收到后回復(fù)ACK,處于close_wait狀態(tài)

(3):服務(wù)器將關(guān)閉前需要發(fā)送信息發(fā)送給客戶端后處于last_ack狀態(tài)

(4):客戶端收到FIN后發(fā)送ack后處于tim-wait而后進(jìn)入close狀態(tài)

為什么要進(jìn)行第三次握手

為了防止服務(wù)器端開(kāi)啟一些無(wú)用的連接增加服務(wù)器開(kāi)銷(xiāo)以及防止已失效的連接請(qǐng)求報(bào)文段突然又傳送到了服務(wù)端

JDK1.8新特性

Lambda表達(dá)式

java也開(kāi)始承認(rèn)了函數(shù)式編程, 就是說(shuō)函數(shù)既可以作為參數(shù),也可以作為返回值,大大的簡(jiǎn)化了代碼的開(kāi)發(fā)

default關(guān)鍵字

打破接口里面是只能有抽象方法,不能有任何方法的實(shí)現(xiàn),接口里面也可以有方法的實(shí)現(xiàn)了

新時(shí)間日期APILocalDate | LocalTime | LocalDateTime

之前使用的java.util.Date月份從0開(kāi)始,我們一般會(huì)+1使用,很不方便,java.time.LocalDate月份和星期都改成了enumjava.util.Date和SimpleDateFormat都不是線程安全的,而LocalDate和LocalTime和最基本的String一樣,是不變類(lèi)型,不但線程安全,而且不能修改。新接口更好用的原因是考慮到了日期時(shí)間的操作,經(jīng)常發(fā)生往前推或往后推幾天的情況。用java.util.Date配合Calendar要寫(xiě)好多代碼,而且一般的開(kāi)發(fā)人員還不一定能寫(xiě)對(duì)。

JDK1.7與JDK1.8 ConcurrentHashMap對(duì)比

(1):JDK1.7版本的ReentrantLock+Segment+HashEntry(數(shù)組)

(2):JDK1.7采用segment的分段鎖機(jī)制實(shí)現(xiàn)線程安全

(3):JDK1.8版本中synchronized+CAS+HashEntry(數(shù)組)+紅黑樹(shù)

(4):JDK1.8采用CAS+Synchronized保證線程安全

(5):查詢時(shí)間復(fù)雜度從原來(lái)的遍歷鏈表O(n),變成遍歷紅黑樹(shù)O(logN)

1.8 HashMap數(shù)組+鏈表+紅黑樹(shù)來(lái)實(shí)現(xiàn)hashmap,當(dāng)碰撞的元素個(gè)數(shù)大于8時(shí) & 總?cè)萘看笥?4,會(huì)有紅黑樹(shù)的引入除了添加之后,效率都比鏈表高,1.8之后鏈表新進(jìn)元素加到末尾

JDK1.8使用synchronized來(lái)代替重入鎖ReentrantLock?

(1):因?yàn)榱6冉档土?,在相?duì)而言的低粒度加鎖方式,synchronized并不比ReentrantLock差

(2):基于JVM的synchronized優(yōu)化空間更大

(3):在大數(shù)據(jù)量下,基于API的ReentrantLock會(huì)比基于JVM的內(nèi)存壓力開(kāi)銷(xiāo)更多的內(nèi)存

JDK1.9新特性

模塊系統(tǒng):

模塊是一個(gè)包的容器,Java 9 最大的變化之一是引入了模塊系統(tǒng)(Jigsaw 項(xiàng)目)。

集合工廠方法

通常,您希望在代碼中創(chuàng)建一個(gè)集合(例如,List 或 Set ),并直接用一些元素填充它。實(shí)例化集合,幾個(gè) “add” 調(diào)用,使得代碼重復(fù)。Java 9,添加了幾種集合工廠方法:

Set<Integer> ints = Set.of(1, 2, 3);
List<String> strings = List.of('first', 'second');

改進(jìn)的 Stream API

Stream 接口中添加了 4 個(gè)新的方法:dropWhile, takeWhile, ofNullable。還有個(gè) iterate 方法的新重載方法

改進(jìn)的 Javadoc:

Javadoc 現(xiàn)在支持在 API 文檔中的進(jìn)行搜索。另外,Javadoc 的輸出現(xiàn)在符合兼容 HTML5 標(biāo)準(zhǔn)。

redis代理集群模式,spring有哪些注解,b+b 紅黑樹(shù)區(qū)別,三次握手,valitile重排序底層代碼,cas 事務(wù)的4個(gè)特性,java8 java11 特性, filter和interceptor的區(qū)別 @autowired原理,dispatcherservlet,分布式事務(wù)解決方案spring都有哪些模塊,fork join隊(duì)列,排序算法,

集合

java的集合框架有哪幾種:

兩種:collection和map,其中collection分為set和List。

List你使用過(guò)哪些

ArrayList和linkedList使用的最多,也最具代表性。

你知道vector和ArrayList和linkedList的區(qū)別嘛

ArrayList實(shí)現(xiàn)是一個(gè)數(shù)組,可變數(shù)組,默認(rèn)初始化長(zhǎng)度為10,也可以我們?cè)O(shè)置容量,但是沒(méi)有設(shè)置的時(shí)候是默認(rèn)的空數(shù)組,只有在第一步add的時(shí)候會(huì)進(jìn)行擴(kuò)容至10(重新創(chuàng)建了數(shù)組),后續(xù)擴(kuò)容按照3/2的大小進(jìn)行擴(kuò)容,是線程不安全的,適用多讀取,少插入的情況

linkedList是基于雙向鏈表的實(shí)現(xiàn),使用了尾插法的方式,內(nèi)部維護(hù)了鏈表的長(zhǎng)度,以及頭節(jié)點(diǎn)和尾節(jié)點(diǎn),所以獲取長(zhǎng)度不需要遍歷。適合一些插入/刪除頻繁的情況。

Vector是線程安全的,實(shí)現(xiàn)方式和ArrayList相似,也是基于數(shù)組,但是方法上面都有synchronized關(guān)鍵詞修飾。其擴(kuò)容方式是原來(lái)的兩倍。

hashMap和hashTable和ConcurrentHashMap的區(qū)別

hashMap是map類(lèi)型的一種最常用的數(shù)據(jù)結(jié)構(gòu),其底部實(shí)現(xiàn)是數(shù)組+鏈表(在1.8版本后變?yōu)榱藬?shù)組+鏈表/紅黑樹(shù)的方式),其key是可以為null的,默認(rèn)hash值為0。擴(kuò)容以2的冪等次(為什么。。。因?yàn)橹挥惺?的冪等次的時(shí)候(n-1)&x==x%n,當(dāng)然不一定只有一個(gè)原因)。是線程不安全的

hashTable的實(shí)現(xiàn)形式和hashMap差不多,它是線程安全的,是繼承了Dictionary,也是key-value的模式,但是其key不能為null。

ConcurrentHashMap是JUC并發(fā)包的一種,在hashMap的基礎(chǔ)上做了修改,因?yàn)閔ashmap其實(shí)是線程不安全的,那在并發(fā)情況下使用hashTable嘛,但是hashTable是全程加鎖的,性能不好,所以采用分段的思想,把原本的一個(gè)數(shù)組分成默認(rèn)16段,就可以最多容納16個(gè)線程并發(fā)操作,16個(gè)段叫做Segment,是基于ReetrantLock來(lái)實(shí)現(xiàn)的

說(shuō)說(shuō)你了解的hashmap吧

hashMap是Map的結(jié)構(gòu),內(nèi)部用了數(shù)組+鏈表的方式,在1.8后,當(dāng)鏈表長(zhǎng)度達(dá)到8的時(shí)候,會(huì)變成紅黑樹(shù),這樣子就可以把查詢的復(fù)雜度變成O(nlogn)了,默認(rèn)負(fù)載因子是0.75,為什么是0.75呢?

我們知道當(dāng)負(fù)載因子太小,就很容易觸發(fā)擴(kuò)容,如果負(fù)載因子太大就容易出現(xiàn)碰撞。所以這個(gè)是空間和時(shí)間的一個(gè)均衡點(diǎn),在1.8的hashmap介紹中,就有描述了,貌似是0.75的負(fù)載因子中,能讓隨機(jī)hash更加滿足0.5的泊松分布。

除此之外,1.7的時(shí)候是頭插法,1.8后就變成了尾插法,主要是為了解決rehash出現(xiàn)的死循環(huán)問(wèn)題,而且1.7的時(shí)候是先擴(kuò)容后插入,1.8則是先插入后擴(kuò)容(為什么?正常來(lái)說(shuō),如果先插入,就有可能節(jié)點(diǎn)變?yōu)闃?shù)化,那么是不是多做一次樹(shù)轉(zhuǎn)化,比1.7要多損耗,個(gè)人猜測(cè),因?yàn)樽x寫(xiě)問(wèn)題,因?yàn)閔ashmap并不是線程安全的,如果說(shuō)是先擴(kuò)容,后寫(xiě)入,那么在擴(kuò)容期間,是訪問(wèn)不到新放入的值的,是不是不太合適,所以會(huì)先放入值,這樣子在擴(kuò)容期間,那個(gè)值是在的)。

1.7版本的時(shí)候用了9次擾動(dòng),5次異或,4次位移,減少hash沖突,但是1.8就只用了兩次,覺(jué)得就足夠了一次異或,一次位移。

concurrentHashMap呢

concurrentHashMap是線程安全的map結(jié)構(gòu),它的核心思想是分段鎖。在1.7版本的時(shí)候,內(nèi)部維護(hù)了segment數(shù)組,默認(rèn)是16個(gè),segment中有一個(gè)table數(shù)組(相當(dāng)于一個(gè)segmeng存放著一個(gè)hashmap。。。),segment繼承了reentrantlock,使用了互斥鎖,map的size其實(shí)就是segment數(shù)組的count和。而在1.8的時(shí)候做了一個(gè)大改版,廢除了segment,采用了cas加synchronize方式來(lái)進(jìn)行分段鎖(還有自旋鎖的保證),而且節(jié)點(diǎn)對(duì)象改用了Node不是之前的HashEntity。

Node可以支持鏈表和紅黑樹(shù)的轉(zhuǎn)化,比如TreeBin就是繼承了Node,這樣子可以直接用instanceof來(lái)區(qū)分。1.8的put就很復(fù)雜來(lái),會(huì)先計(jì)算出hash值,然后根據(jù)hash值選出Node數(shù)組的下標(biāo)(默認(rèn)數(shù)組是空的,所以一開(kāi)始put的時(shí)候會(huì)初始化,指定負(fù)載因子是0.75,不可變),判斷是否為空,如果為空,則用cas的操作來(lái)賦值首節(jié)點(diǎn),如果失敗,則因?yàn)樽孕?,?huì)進(jìn)入非空節(jié)點(diǎn)的邏輯,這個(gè)時(shí)候會(huì)用synchronize加鎖頭節(jié)點(diǎn)(保證整條鏈路鎖定)這個(gè)時(shí)候還會(huì)進(jìn)行二次判斷,是否是同一個(gè)首節(jié)點(diǎn),在分首節(jié)點(diǎn)到底是鏈表還是樹(shù)結(jié)構(gòu),進(jìn)行遍歷判斷。

concurrentHashMap的擴(kuò)容方式

1.7版本的concurrentHashMap是基于了segment的,segment內(nèi)部維護(hù)了HashEntity數(shù)組,所以擴(kuò)容是在這個(gè)基礎(chǔ)上的,類(lèi)比hashmap的擴(kuò)容,

1.8版本的concurrentHashMap擴(kuò)容方式比較復(fù)雜,利用了ForwardingNode,先會(huì)根據(jù)機(jī)器內(nèi)核數(shù)來(lái)分配每個(gè)線程能分到的busket數(shù),(最小是16),這樣子可以做到多線程協(xié)助遷移,提升速度。然后根據(jù)自己分配的busket數(shù)來(lái)進(jìn)行節(jié)點(diǎn)轉(zhuǎn)移,如果為空,就放置ForwardingNode,代表已經(jīng)遷移完成,如果是非空節(jié)點(diǎn)(判斷是不是ForwardingNode,是就結(jié)束了),加鎖,鏈路循環(huán),進(jìn)行遷移。

hashMap的put方法的過(guò)程

判斷key是否是null,如果是null對(duì)應(yīng)的hash值就是0,獲得hash值過(guò)后則進(jìn)行擾動(dòng),(1.7是9次,5次異或,4次位移,1.8是2次),獲取到的新hash值找出所在的index,(n-1)&hash,根據(jù)下標(biāo)找到對(duì)應(yīng)的Node/entity,然后遍歷鏈表/紅黑樹(shù),如果遇到hash值相同且equals相同,則覆蓋值,如果不是則新增。如果節(jié)點(diǎn)數(shù)大于8了,則進(jìn)行樹(shù)化(1.8)。完成后,判斷當(dāng)前的長(zhǎng)度是否大于閥值,是就擴(kuò)容(1.7是先擴(kuò)容在put)。

為什么修改hashcode方法要修改equals

都是map惹的禍,我們知道在map中判斷是否是同一個(gè)對(duì)象的時(shí)候,會(huì)先判斷hash值,在判斷equals的,如果我們只是重寫(xiě)了hashcode,沒(méi)有順便修改equals,比如Intger,hashcode就是value值,如果我們不改寫(xiě)equals,而是用了Object的equals,那么就是判斷兩者指針是否一致了,那就會(huì)出現(xiàn)valueOf和new出來(lái)的對(duì)象會(huì)對(duì)于map而言是兩個(gè)對(duì)象,那就是個(gè)問(wèn)題了

TreeMap了解嘛

TreeMap是Map中的一種很特殊的map,我們知道Map基本是無(wú)序的,但是TreeMap是會(huì)自動(dòng)進(jìn)行排序的,也就是一個(gè)有序Map(使用了紅黑樹(shù)來(lái)實(shí)現(xiàn)),如果設(shè)置了Comparator比較器,則會(huì)根據(jù)比較器來(lái)對(duì)比兩者的大小,如果沒(méi)有則key需要是Comparable的子類(lèi)(代碼中沒(méi)有事先check,會(huì)直接拋出轉(zhuǎn)化異常,有點(diǎn)坑啊)。

LinkedHashMap了解嘛

LinkedHashMap是HashMap的一種特殊分支,是某種有序的hashMap,和TreeMap是不一樣的概念,是用了HashMap+鏈表的方式來(lái)構(gòu)造的,有兩者有序模式:訪問(wèn)有序,插入順序,插入順序是一直存在的,因?yàn)槭钦{(diào)用了hashMap的put方法,并沒(méi)有重載,但是重載了newNode方法,在這個(gè)方法中,會(huì)把節(jié)點(diǎn)插入鏈表中,訪問(wèn)有序默認(rèn)是關(guān)閉的,如果打開(kāi),則在每次get的時(shí)候都會(huì)把鏈表的節(jié)點(diǎn)移除掉,放到鏈表的最后面。這樣子就是一個(gè)LRU的一種實(shí)現(xiàn)方式。

數(shù)據(jù)結(jié)構(gòu)+算法

TODO(未完待續(xù))

總結(jié)

內(nèi)容過(guò)于硬核了,導(dǎo)致很多排版細(xì)節(jié),我沒(méi)辦法做得像其他期一樣精致了,大家見(jiàn)諒。

涉及的內(nèi)容和東西太多了,可能很多都是點(diǎn)到為止,也有很多不全的,也有很多錯(cuò)誤的點(diǎn),已經(jīng)快3W字了,我校驗(yàn)實(shí)在困難,我會(huì)放在GitHub上面,大家可以跟我一起更新這個(gè)文章,造福后人吧。

搞不好下次我需要看的時(shí)候,我都得看著這個(gè)復(fù)習(xí)了。

我是敖丙,一個(gè)在互聯(lián)網(wǎng)茍且偷生的工具人。

你知道的越多,你不知道的越多人才們的  【三連】 就是丙丙創(chuàng)作的最大動(dòng)力,我們下期見(jiàn)!

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多