2021-04-12

 

谈谈你对集合的理解?

1 集合属于java的util包

2 主要包括两大类 一类是collection 接口以及它的衍生子接口,一类是map接口以及它的一系列衍生接口

3 collection 的话有 list set queue 三大类子接口,比较常用的有 LinkedList,ArrayList HashSet blockingqueue

Map的话 比较常用的有HashMap Hashtable ConcurrentHashMap

底层原理  下面

1 Arraylist 和linkedliskt的区别

Arraylist和linkedliskt最大的区别是arrayList是基于数组实现的而linkedlist是基于链表实现的,先说Arraylist,由于它底层是数组,所以很多性质跟数组是相通的,比如在查询和插入删除的效率方面。

在查询方面,由于数组在存储数据方面,是通过划分相等内存空间大小来存储数据的,所以它的查询算法是通过下标乘以内存大小来进行的,由于arraylist本身是实现了支持随机访问的标记接口randomaccess的,所以在查询方面,arraylist效率仅仅跟下标值相关,在查询不同存储位置的数据时,效率变化不大,所以查询效率是0(1)的,与之相对应的是它在插入和删除方面,由于数组的性质,插入和删除的数据会导致数组数据存储位置移动,并且是越靠前的插入导致的移动就越多,下标大于下标法插入的指定下标都会移动,这样就导致了插入删除效率会随着下标变化发生变化,这样使得插入删除速率的时间复杂度是哦(n)的。

所以在插入的尽量默认下标插入,当插入发生在数组末尾时效率最好。

Linkedlist的底层是链表,所以它的性质基于链表。它的时间复杂度跟arrayList的正好相反。查询时需要从头进行next所以时间复杂度为o(n),插入是仅仅需要断裂重连所以时间复杂度为o(n)。10 两倍

2.HashMap 的底层实现

Hashmap的底层原理在1.7和1.8中是不同的,先说1.7,在1.7中hashmap的底层是数组加链表,hashmap虽然是通过key-value的形式来进行数据存储,但是实际上,在底层,key-value一起作为一个对象将引用存储在了entry数组内。Hashmap在存储数据时,会先计算当前存储对象的key的hashcode值,当然如果key为null的话,会存储在数组的0位,通过计算key的hashcode值和数组长度进行取余并进行位运算。来确定当前数据它具体存储在数组的位置,如果当前位置已经有一个引用或者存在链表的话,那么他就会进key的比较,如果当前的key都不相等,那么他就会使用头插法来进行插入。头插法以后会进行引用地址的切换。

Hashmap的扩容是基于数组的两倍进行扩容,目的并不单单是为了加长数组,还是为了缩短链表的长度。当hashmap打到0.75的扩容系数以后会进行扩容,当然是在当前要插入的位置有值得情况下才会进行扩容,如果当前插入的地方没有值,那么他也是不会进行扩容的,当新的数组生成以后,hashmap会进行数据转义,但是在转移的过程中,他会进行基于新数组的长度的重新hash,这样会使他的链表长度剪短。并且链表数据也会进行倒置。

Hashmap有一个快速失败机制,当操作数对不上的时候回报异常,如果一个线程在修改的话。1.7的hashmap会死循环。1.8会出现数据覆盖的现象。

1.8的hashmap原理

1.8的hashmap底层使用的是数组+链表+红黑树,在链表长度达到8时,再进行插入的话,会判断当前数组的长度是否达到64,如果没有达到64,那么会进行数组的扩容,如果达到64,那么会进行链表向红黑树的转换(反正要遍历链表所以使用了尾插法,并且由于使用了红黑树导致他的hash算法得到简化,提升了cup的性能,转移数据的话,1.7是一个一个,1.8是先遍历计算,一次性转移)。

  1. ConcurrentHashMap

由于HashMap在并发时线程是不安全:

1.在JDK1.7中,当并发执行扩容操作时会造成环形链和数据丢失的情况。

2.在JDK1.8中,在并发执行put操作时会发生数据覆盖的情况。(先进性hash后进行put时,如果线程被挂起,那么另一个线程先put了,当前线程不会重新hash而是直接put 导致覆盖)

4.1.7中ConcurrentHashMap是怎么保证并发安全的。

利用unsafe+reentrantLock+分段思想来实现的

Unsale主要是并发安全的给数组的某个位置赋值

 并发安全的获取数组某个位置的值,

并且利用cas并发安全的修改对象的属性

reentrantLock是加在segment数组上面,每个segment数组都包含一个小型的hashmap都可以作为一段进行加锁这样就提高了效率。

5 .1.7中ConcurrentHashMap底层原理

ConcurrentHashMap的底层是由两层嵌套数组来实现的(默认长度16 扩展因子0.75 并发级别16)

最外层数组是segment数组,而segment对象有一个属性是hashentry数组,当调用concurrenthashmap的put方法时,会先对key和segment数组进行hash,算出数据存储在哪个segment对象里面,如果当前对象为空则利用自旋锁的方式生成一个segment对象(仿造segment0模板 默认一个segment有两个hashentry数组)。

然后调用segment对象的put,调用之前先加锁。类似hashmap的put方式。

6 .1.8中ConcurrentHashMap怎么保证并发安全的

利用unsafe+synchronized来实现的

Unsafe:修改属性和某个数组的获取与赋值,synchronized负责在操作某个位置时进行控制,比如链表和红黑树的插入。

 

(原理)底层是数组链表红黑树

当concurrenthashmap进行put时,也是根据key计算数组下标,如果当前位置没有元素,那么就会通过cas自旋进行赋值

如果该位置有元素,就会加synchronized,加锁成功以后会判断该元素的类型,如果是链表就加到链表,如果是红黑树就加到红黑树上,添加以后判断是否需要进行树化。

 

7. 1.7中ConcurrentHashMap1.8中ConcurrentHashMap的区别

8中没有分段锁,扩容性能更高 8中元素计数通过了countercell数组的帮助。

 

 

 

 

 

 

 

谈谈你对多线程的理解?

1 多线程属于 java.lang包的rannable接口 Thread类 

public class RunableTest{  

//继承Thread类并重写run方法  

public static class RunableTask implements Runnable{  

@Override  

public void run(){  

System.out.printlan("I am a child theread");  

}  

}  

public static void mian(String[] args) throws InterruptedException{  

//创建线程  

RunableTask task = new RunableTask();  

//启动线程  

new Thread(task).start();  

new Thread(task).start();  

}  

}

 

 

public class ThreadTest{  

//继承Thread类并重写run方法  

public static class MyThread extends Thread{  

@Override  

public void run(){  

System.out.printlan("I am a child theread");  

}  

}  

public static void mian(String[] args){  

//创建线程  

MyThread thread = new MyThread;  

//启动线程  

thread.start();  

}  

}

 

 

java.util.concurrent{BlockingQueue  阻塞队列
Callable 创建线程的  Future 操纵callable接口的Future Task的父接口 Executor 线程池接口 }包以及java.util.concurrent.atomic{AtomicInteger  AtomicReference   AtomicStampedReference cas理论 解决aba问题}

java.util.concurrent.locks {Condition 线程通信 Lock  ReadWriteLock 读写锁}

 

 

 

java.util.concurrent.locks {Condition 线程通信 Lock  ReadWriteLock 读写锁}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BlockingQueue  阻塞队列【

 

 

 

 

 

Callable 创建线程的  Future 操纵callable接口的Future Task的父接口

 

 

public class CallableDemo {

    //实现Callable接口

    class MyThread implements Callable<Integer> {

        @Override

        public Integer call() throws Exception {

            System.out.println("callable come in ...");

            return 1024;

        }

    }

    

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //创建FutureTask类,接受MyThread。    

        FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());

        //将FutureTask对象放到Thread类的构造器里面。

        new Thread(futureTask, "AA").start();

        int result01 = 100;

        //用FutureTask的get方法得到返回值。

        int result02 = futureTask.get();

        System.out.println("result=" + (result01 + result02));

    }

}

 

 

 

 Executor 线程池接口【

 

 

 

 

 

 

 

 

 

 

java.util.concurrent.atomic{AtomicInteger  AtomicReference   AtomicStampedReference cas理论 解决aba问题}

 

Cas原理

(cas是一无锁的算法,通过原值和预期值进行比较,如果相等的话就会进行更新,以此用来保证共享变量的原子操作,他本身对应的是一个汇编指令来保证原子性atomic compareXchange指令  Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference类来解决ABA问题

对CAS的理解,CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

Cas的核心类是unsafe类,unsafe直接用来操作更底层的资源。

他本身是一个并发的原语指令。是原子的。Atomic compareXchange指令的汇编指令

Cas缺点:导致aba问题 循环开销大 只能保证一个共享变量的原子操作。

ABA问题的解决思路就是使用版本号

从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference类来解决ABA问题

这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值

 

 

 

 

 

2  为了提高CPU的使用率,进而提高程序效率

3 创建多线程的方式的方面的话 线程生命周期

4 并发安全相关内容 锁相关 集合相关

4 我工作中使用到的多线程知识  同步数据

线程池实际经验

1.

多线程发短信

搭建分布式事务平台

 

 

 

 

 

 

 

 

谈谈你对数据库优化的理解?

 

优化的实际案例

1 大接口拆分

2 前端异步

3 侵入式事务

4 取消分布式锁

5 合并数据库连接

6 做一个异步线程

7 缓存 有些数据是不是真的需要实时更新

 

看一下这个接口的执行是多少,然后如果是新业务也就是不是老接口的话,

假如真的有一个很大的接口,关键运行时间也很长,举个例子,比如活动首页,需要展示排行榜,显示抽奖机会,显示当前活动的一些动态信息,等等吧需要很多数据,而这些数据需要几个表的数据提供支持,那么就得去跟前端沟通一下,能不能先展示部分数据,然后我们后台在定义的接口的时候,根据业务将接口拆分的零散一点,以便将来扩展,还有就是能加缓存的加缓存,等等。然后就是说核心接口如果真的是效率很慢,那么是不是考虑取消分布式锁,评估取消分布式锁的风险,然后或者说将声明式事务改为侵入式事务(万不得已),然后看一下当前业务是不是真的需要这么多数据库连接,看一下这个数据连接能不能减少,或者说如果是走的业务定义不相关的多个业务,看看能不能做一个异步线程。

基本上优化的话,就这么多吧,再然后就是数据库优化的话也就是找出来最耗时的并且是常用的做不了异步的sql优化一下。使用explan这个工具。

 

数据字段进行扩充 索引 数据库连接数 业务场景已经过去了 异步线程合并结果(子线程)

 

脑图 总结自己的话

谈谈你对redis的理解?

复习图 自己的话

谈谈你对spring的理解?

Spring的事务以及隔离级别

事务特性(4种):

原子性 (atomicity):强调事务的不可分割.

一致性 (consistency):事务的执行的前后数据的完整性保持一致.

隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰

持久性(durability) :事务一旦结束,数据就持久到数据库

————————————————

脏读

脏读又称无效数据的读出,是指在数据库访问中,事务T1将某一值修改,然后事务T2读取该值,此后T1因为某种原因撤销对该值的修改,这就导致了T2所读取到的数据是无效的。

不可重复读

在一个事务中多次读取同一个数据时,结果出现不一致。

在 MySQL InnoDB 中,Repeatable Read 隔离级别使用 MVCC 来解决不可重复读问题。

 

幻读

在一个事务中使用相同的 SQL 两次读取,第二次读取到了其他事务新插入的行。

 

Required传播 :没有就新建

如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。

PROPAGATION_REQUIRED

如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。

PROPAGATION_SUPPORTS

支持当前事务,如果当前没有事务,就以非事务方式执行。

PROPAGATION_MANDATORY

使用当前的事务,如果当前没有事务,就抛出异常。

PROPAGATION_REQUIRES_NEW

新建事务,如果当前存在事务,把当前事务挂起。

PROPAGATION_NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER

以非事务方式执行,如果当前存在事务,则抛出异常。

PROPAGATION_NESTED

如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

读取未提交内容

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

读取提交内容

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

可重读

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

可串行化

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

Springboot的理解?

自动加载原理

项目搭建过程

项目业务流程

待总结

包括对接花点时间公司鲜花销售。

--报名参加活动,积分换购活动。

--活动表用户(用户信息,参加活动的时间,参加活动的月份),每天定时去轮询全表(分次分页查询)(怕多线程批量扣除的时候出现问题)是否满足扣款条件。如果满足扣款条件,那么去定时生成支付订单,带着支付订单唯一流水号去扣减积分,当扣减成功的时候,会生成本月的一个主订单,四个子订单,然后如果用户修改地址那么下个月生效。扣款当天不让修改。并且有一个查询扣款结果的定时,每天都会执行。然后扣款失败的话,

也有可能是积分不够用了,那么本地监控扣款回执,如果是真的失败了修改

订单状态,然后后台管理系统有发起重新支付,查询支付结果的按钮。积分系统记录了扣减日志。

 

公司周年庆活动:拉新用户注册(新用户必须绑定微信号,并且必须经过手机号验证码验证才算是一个真实的人)、消费送抽奖机会。为期两个个月。

有空奖(只有黑名单才会出空奖)

拉新用户注册,每10个人送一次抽奖机会,最低能够获得10元话费,10个积分。

(随机数)当10元话费达到incr 1000人时会陆续发短信给业务,达到20000时,会直接关闭活动(达到活动资金上限)

10元 50元 100元 500元加油卡 苹果耳机 苹果12(礼品公司采购的)

 临时上架产品,购买指定产品,获得抽大奖资格。

抽奖的话有一个大奖奖池,redis 的set实现的。然后每次随机从里面随机取一个删除。然后是为了保证随机,每9的倍数才有机会获取直接从大奖池里面抽奖的资格。这个数字我们后台管理系统能调整。然后是拉新只会抽小奖品

为了防止人盗刷,也就是在抽奖哪里会有一个手机号黑名单。

然后数据库存在一个跟redis对应的奖池表用来预防redis宕机。重新加载进redis。

新用户新增20多万,营业额1000多万(300多的奶粉大礼包)

 

然后是

合伙人活动:拉新用户注册、消费送抽奖机会。长期活动(换汤不换药)

app主题换肤活动:拉新用户注册、消费送抽奖机会。长期活动

主题换肤是app出了几款皮肤,只要你拉新用户注册、消费送抽奖机会,营销系统仅仅记录抽奖机会,具体抽中那个是妈妈购给的。那边那个用户中心。

 

拆红包活动:庆新年,邀请人注册送代现金10元。为期2个月。

拆红包活动,从10人档,15分档,20人档 100人档。

用户点击拆,生成一条红包记录,

总账户表,账户详情表,邀请记录表。配置表。

然后,红包金额是在5-6间随机生成。单独拿出来1块用来备用100档

然后每次邀请一个人就会在剩余金额上面乘以0.1系数。

当用户已经完成了10人档,那么就会滑档。

而且每天的10人档15人档 20人档都是限量的,100人不限量。

总数限量。达到总数会自动关闭活动。

 

 

 

代金券活动:符合领取条件领取代金券。长期活动。

就是说首页banner图完成单次消费300元 送10元代金券。

然后我本地就是加载一些送券的条件,比如说手机号单号双号,手机号白名单

地址是北京的。地址表

然后消费的时候是支付中心发个直连的锁定,然后去消费,消费成功以后,发mq消息通知我解锁。如果消费失败,我这边就一直锁定中。

 

分布式事务理解

直接看笔记

分布式锁 原理

Mq

你们项目里怎么使用的消息队列?

同步用户中心的用户信息,目的是为了系统解耦,他的用户数据比如好几个系统都需要,他不能每次都新开一个http连接。再有的话坏处是每次他们改密码的话,我们得不到通知就会出现问题。

发布订阅是?

直接看笔记

 

 

  • 基础部分

1.==和equals的区别

== : 它的作用是判断两个对象的地址是不是相等。即判断两个对象是不是同一个对象。(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)

equals() 方法存在两种使用情况:

情况 1:类没有覆盖 equals()方法。则通过 equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。使用的默认是 Object类equals()方法。

情况 2:类覆盖了 equals()方法。一般,我们都覆盖 equals()方法来两个对象的内容相等;若它们的内容相等,则返回 true(即,认为这两个对象相等)。

2.hashCode()与 equals()

①、hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在 JDK 的 Object 类中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。另外需要注意的是: Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法通常用来将对象的 内存地址 转换为整数之后返回。

②、为什么重写 equals 时必须重写 hashCode 方法?

如果两个对象相等,则 hashcode 一定也是相同的。两个对象相等,对两个对象分别调用 equals 方法都返回 true。但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖。

(解释:因为hashmap和hashset会比较hashcode值,如果两个对象的hashcode值不同但是存入的值是相同的,hashmap会当成两个不同值插入)

3.Java中Class类和Object类常用方法

①、class类常用方法

1、Class obj= Class.forName("XXXX");获取一个class对象。

2、Object ShapesInstance=obj.newInstance();获取一个对象实例。

②、object类是Java中其他所有类的祖先,位于java.lang包中,java.lang包包含着Java最基础和核心的类,一共有13个方法。

1. Object方法

Object类的构造方法。

2. registerNatives方法

对本地方法进行注册。

3.clone方法

保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。

4.getClass方法

final方法,获得运行时类型。

5.toString方法

该方法用得比较多,一般子类都有覆盖。

6.finalize方法

该方法用于释放资源。当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

7.equals方法

该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。

8.hashCode方法

该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。(可参考上面提到的hashcode方法)

9.wait方法

wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。调用该方法后当前线程进入睡眠状态,直到以下事件发生。

(1)其他线程调用了该对象的notify方法。

(2)其他线程调用了该对象的notifyAll方法。

(3)其他线程调用了interrupt中断该线程。

(4)时间间隔到了。

此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。

10.wait(long timeout)方法

使线程停止等待,事件同上。事件单位毫秒。

11. wait(long timeout, int nanos)方法

使线程停止等待,事件同上。区别:int nanos是代表了纳秒级别(1毫秒 = 1000 微秒 = 1000 000 纳秒),能更好的进行控制。

12.notify方法

该方法唤醒在该对象上等待的某个线程。

13.notifyAll方法

该方法唤醒在该对象上等待的所有线程。

11.Java异常

在 Java 中,所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类。Throwable: 有两个重要的子类:Exception(异常) 和 Error(错误) ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。

1、Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。

2、Exception(异常):是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeException。RuntimeException 异常由 Java 虚拟机抛出,包括异常:NullPointerException(要访问的变量没有引用任何对象时,抛出该异常)、ArithmeticException(算术运算异常,一个整数除以 0 时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。

常见问题:

①、try 块: 用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。(必须要有其中一个)

②、catch 块: 用于处理 try 捕获到的异常。

③、finally 块: 无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。

 

在以下 4 种特殊情况下,finally 块不会被执行:

1、在 finally 语句块第一行发生了异常。 因为在其他行,finally 块还是会得到执行

2、在前面的代码中用了 System.exit(int)已退出程序。 exit 是带参函数 ;若该语句在异常语句之后,finally 会执行

3、程序所在的线程死亡。

4、关闭 CPU。

 

  • 集合

1 Arraylist 和linkedliskt的区别

Arraylist和linkedliskt最大的区别是arrayList是基于数组实现的而linkedlist是基于链表实现的,先说Arraylist,由于它底层是数组,所以很多性质跟数组是相通的,比如在查询和插入删除的效率方面。

在查询方面,由于数组在存储数据方面,是通过划分相等内存空间大小来存储数据的,所以它的查询算法是通过下标乘以内存大小来进行的,由于arraylist本身是实现了支持随机访问的标记接口randomaccess的,所以在查询方面,arraylist效率仅仅跟下标值相关,在查询不同存储位置的数据时,效率变化不大,所以查询效率是0(1)的,与之相对应的是它在插入和删除方面,由于数组的性质,插入和删除的数据会导致数组数据存储位置移动,并且是越靠前的插入导致的移动就越多,下标大于下标法插入的指定下标都会移动,这样就导致了插入删除效率会随着下标变化发生变化,这样使得插入删除速率的时间复杂度是哦(n)的。

所以在插入的尽量默认下标插入,当插入发生在数组末尾时效率最好。

Linkedlist的底层是链表,所以它的性质基于链表。它的时间复杂度跟arrayList的正好相反。查询时需要从头进行next所以时间复杂度为o(n),插入是仅仅需要断裂重连所以时间复杂度为o(n)。10 两倍

2.HashMap 的底层实现

Hashmap的底层原理在1.7和1.8中是不同的,先说1.7,在1.7中hashmap的底层是数组加链表,hashmap虽然是通过key-value的形式来进行数据存储,但是实际上,在底层,key-value一起作为一个对象将引用存储在了entry数组内。Hashmap在存储数据时,会先计算当前存储对象的key的hashcode值,当然如果key为null的话,会存储在数组的0位,通过计算key的hashcode值和数组长度进行取余并进行位运算。来确定当前数据它具体存储在数组的位置,如果当前位置已经有一个引用或者存在链表的话,那么他就会进key的比较,如果当前的key都不相等,那么他就会使用头插法来进行插入。头插法以后会进行引用地址的切换。

Hashmap的扩容是基于数组的两倍进行扩容,目的并不单单是为了加长数组,还是为了缩短链表的长度。当hashmap打到0.75的扩容系数以后会进行扩容,当然是在当前要插入的位置有值得情况下才会进行扩容,如果当前插入的地方没有值,那么他也是不会进行扩容的,当新的数组生成以后,hashmap会进行数据转义,但是在转移的过程中,他会进行基于新数组的长度的重新hash,这样会使他的链表长度剪短。并且链表数据也会进行倒置。

Hashmap有一个快速失败机制,当操作数对不上的时候回报异常,如果一个线程在修改的话。1.7的hashmap会死循环。1.8会出现数据覆盖的现象。

1.8的hashmap原理

1.8的hashmap底层使用的是数组+链表+红黑树,在链表长度达到8时,再进行插入的话,会判断当前数组的长度是否达到64,如果没有达到64,那么会进行数组的扩容,如果达到64,那么会进行链表向红黑树的转换(反正要遍历链表所以使用了尾插法,并且由于使用了红黑树导致他的hash算法得到简化,提升了cup的性能,转移数据的话,1.7是一个一个,1.8是先遍历计算,一次性转移)。

  1. ConcurrentHashMap

由于HashMap在并发时线程是不安全:

1.在JDK1.7中,当并发执行扩容操作时会造成环形链和数据丢失的情况。

2.在JDK1.8中,在并发执行put操作时会发生数据覆盖的情况。(先进性hash后进行put时,如果线程被挂起,那么另一个线程先put了,当前线程不会重新hash而是直接put 导致覆盖)

4.1.7中ConcurrentHashMap是怎么保证并发安全的。

利用unsafe+reentrantLock+分段思想来实现的

Unsale主要是并发安全的给数组的某个位置赋值

 并发安全的获取数组某个位置的值,

并且利用cas并发安全的修改对象的属性

reentrantLock是加在segment数组上面,每个segment数组都包含一个小型的hashmap都可以作为一段进行加锁这样就提高了效率。

5 .1.7中ConcurrentHashMap底层原理

ConcurrentHashMap的底层是由两层嵌套数组来实现的(默认长度16 扩展因子0.75 并发级别16)

最外层数组是segment数组,而segment对象有一个属性是hashentry数组,当调用concurrenthashmap的put方法时,会先对key和segment数组进行hash,算出数据存储在哪个segment对象里面,如果当前对象为空则利用自旋锁的方式生成一个segment对象(仿造segment0模板 默认一个segment有两个hashentry数组)。

然后调用segment对象的put,调用之前先加锁。类似hashmap的put方式。

6 .1.8中ConcurrentHashMap怎么保证并发安全的

利用unsafe+synchronized来实现的

Unsafe:修改属性和某个数组的获取与赋值,synchronized负责在操作某个位置时进行控制,比如链表和红黑树的插入。

 

(原理)底层是数组链表红黑树

当concurrenthashmap进行put时,也是根据key计算数组下标,如果当前位置没有元素,那么就会通过cas自旋进行赋值

如果该位置有元素,就会加synchronized,加锁成功以后会判断该元素的类型,如果是链表就加到链表,如果是红黑树就加到红黑树上,添加以后判断是否需要进行树化。

 

7. 1.7中ConcurrentHashMap1.8中ConcurrentHashMap的区别

8中没有分段锁,扩容性能更高 8中元素计数通过了countercell数组的帮助。

  • 多线程
  1. 线程和进程

进程:资源分配的基本单位,线程:运行的基本单位,调度的基本单位。

进程:进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。例如:在 windows 中通过查看任务管理器的方式,我们就可以清楚看到 window 当前运行的进程(.exe 文件的运行)

线程:线程与进程相似,但线程是一个比进程更小的执行单位。一个进 程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

  • 线程的生命周期和状态

NEW 初始状态  new 以后没有调用start以前

RUNNABLE 就绪状态 调用start以后未获得cup资源

Running 运行状态 执行run方法

Blocked阻塞状态 —、无时间限制的waiting (第一wait方法 第二 join方法等)二、有时间限制的等待 sleep wait(带等待时间)等

Terminated 终止状态

Run以后 或者异常 interrupt方法的使用

interrupt方法的使用:

 

 

多线程的创建

1继承thred类 重写run    extends thred

2 实现runnable接口 implements runnable

3 实现callable接口 implements callable

4 线程池

Thread类和Runnable接口或者Callable接口的区别

Thread线程类已经继承Thread类了,就不能再继承其他类;

Runnable接口或者Callable接口:

优点:线程类只是实现了接口,还可以继承其他类;

Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、(Future实现类 runnable实现类)FutureTask配合可以用来获取异步执行的结果。

 

线程池

①.线程池的好处:

(降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

提高响应速度:当任务到达时,任务可以不需要的等到线程创建就能立即执行。

提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程重复利用,提高效率。并且便于统一管理。

 

Sleep wait join yield 区别

Sleep 会使线程进入阻塞 但是不会释放锁

Wait 不带时间时,调用之前必须获取锁,

yield 没有时间 使当前线程回到可以重新执行的状态

Join 是当前线程等待join结束以后才可以执行

 

线程安全的理解

线程包含三个核心概念 原子性--(同时成功同时失败) 可见性---多个线程对共享变量操作时,一个线程的修改对其他线程是可见的

有序行--代码执行的顺序是一定的

如何实现线程安全--加锁,加synchronized 加lock

 

锁实例对象和锁class对象的区别 锁括号内的对象的区别

同一个对象不能为两个线程访问 锁的如果是类 那么这个类的所有对象都不可以 锁的如果是类或者静态方法那么就是锁的类如果锁的是非静态方法那么就是锁的对象。

 

死锁产生的原因 如何防止死锁

例如:线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。

 

 

守护线程的理解

Gc线程是守护线程,当非守护线程结束以后 守护线程会自己结束

 

Threadlocal的原理和使用场景

Threadlocal内存泄露原因,如何避免

并发并行串行的区别

Volatile

为什么使用线程池 线程池的参数

线程池的处理流程

线程池中阻塞队列的作用 为什么是先添加队列而不是先创建最大线程

 

Jmm java内存模型

是一种定义线程和主内存之间访问方式。线程有自己缓存,线程对数据的操作是在自己的内存中进行,完了以后再刷新到主内存里,这期间各个线程之间关于数据的操作是不可见的,线程之间通信是用过主内存完成。

Jmm有三大特性,可见性 原子性 有序性

 

Volatile

(volatile的话是一个轻量级的同步机制,对于jmm模型所要求的三大重要特性来说,它仅仅能实现可见性,和禁止指令重排序,并不能保证原子性)

Volatile是java虚拟机提供的轻量级的同步机制,保证可见性 不保证原子性 保证有序性(禁止指令重排)

添加了volatile以后 一个线程对共享变量的改变会使其他线程可见。

Volatile不保证原子性。比如多线程操作num++;会出现值被覆盖的情况,意思是当一个线程在对变量进行一系列操作时,其他线程也可以对他进行操作。

怎么解决原子性:使用atomic包

指令重排:多线程环境下由于编译器优化重排的存在,两个线程中使用的变量一致性是无法保证的 。

 

 

通过加内存屏障指令的方式来实现可见性和有序性

对于可见性问题 可以添加synchronize 关键字和volatile关机字。

 

 

单例模式:

单例模式里面禁止指令重排。 主要是new对象是分三步完成的

1 分配对象内存空间

2 初始化对象

3 进行内存地址的分配

原因是2 和3 不存在依赖关系 有可能发生指令重排

 

Private volatile static single 对象;

 

Public static Single getSingle(){

 

If (对象== null){

Synchronized(对象。class){

If(对象== null){

对象= new 对象

}

}

 

}

Return 对象;

}

;.

 

 

 

Cas原理

(cas是一无锁的算法,通过原值和预期值进行比较,如果相等的话就会进行更新,以此用来保证共享变量的原子操作,他本身对应的是一个汇编指令来保证原子性atomic compareXchange指令  Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference类来解决ABA问题

对CAS的理解,CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

Cas的核心类是unsafe类,unsafe直接用来操作更底层的资源。

他本身是一个并发的原语指令。是原子的。Atomic compareXchange指令的汇编指令

Cas缺点:导致aba问题 循环开销大 只能保证一个共享变量的原子操作。

ABA问题的解决思路就是使用版本号

从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference类来解决ABA问题

这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值

 

 

Concurrentmodificationexception 并发修改异常

arrayList类不安全的解决方案:1换用vector集合

解决arrayList不安全 2 可以使用collections类进行synchronizedList方法包装。

使用copyonwritearraylist

底层是一个volatile修饰的Object数组

写时复制

读写分离的思想

add之前先加一个reetrantLock 每次add先复制扩容一个长度

好处是不影响原有的list的读操作,因为写操作并没有改变原有的数组,而是复制了一个新的数组

 

Hashset不是安全 底层是一个hashmap

使用copyonwritearrayset

底层是一个coryonwriteArraylist

 

 

  • Java 锁

reetrandLock 可重入锁(递归锁) 默认非公平

公平 非公平锁

公平 按顺序来 先查看维护的等待队列  为空的时候尝试占用锁,不为空的时候直接先进先出的去排队

非公平锁 有可能后申请的线程先获取锁

先尝试获取锁,没有获取到再排队

优点是-------吞吐量大,效率高

 

 

Synchronized 和lock的区别

可重入锁:在外层获取到锁以后在内层会自动获取到锁。

锁方法调用锁方法

防止死锁

 

自旋锁:

尝试获取锁的线程不会阻塞等待,而是会采用循环的方式去尝试获取锁,减少上下文切换的消耗,但是循环会浪费cpu性能

 

独占锁(写锁)Synchronized 和lock

共享锁(读锁)reentrantLockReadWriteLock锁 读是共享 写时是独占

readLock writelock

两个锁

互斥锁

 

Synchronized  底层原理

 

 

reetrandLock  底层原理

 

线程池

阻塞队列:当队列元素是空的时候 获取元素的操作将被阻塞

当队列元素是满的时候,插入元素的操作将被阻塞

 

arrayblockingQueue:基于数组 先进先出 有界

 

 

 

 

linkedBlockingQueue:基于链表 先进先出 吞吐量高于数组阻塞

 

常用方法

Add remove element(检查最首元素 空不空)

Offer peek poll  带时间的放 取

Put take 放不进去一直放 取不到一直取

 

 

 

 

synchronousQueue:不存储元素 每个插入操作必须等待另一个线程调用移除操作,吞吐量更高

如果有一个put以后 第二个put必须等到take

 

 

阻塞队列好的一面

不用关心什么时候需要阻塞 什么时候需要唤醒。阻塞队列给管理了

 

不得不阻塞,你如何管理

 

阻塞队列用在哪里 生产者消费者

线程池

消息队列

 

虚假唤醒:??使用while代替视同if 使用await唤醒时

 

 

 

 

 

 

 

  • Maven

Maven 是什么?

  • 依赖管理系统
  • 多模块构建

常用命令

  • mvn clean :清除项目目录中的生成结果。
  • mvn package :根据项目生成的 jar/war 等。
  • mvn install :在本地 Repository 中安装 jar 。

 

 

  •  
  • Jvm
  1.  jvm 的主要组成部分?及其作用?

 

Jvm组成部分主要有类加载器 执行引擎,本地接口 运行时数据区

运行时数据区又分为栈、堆、方法区、本地方法栈、程序计数器。

其中栈

主要存储局部变量表、操作数栈、动态链接,方法出口信息。

堆的作用:

存储数组和对象

方法区的作用:

主要存储静态变量 常量 类信息(接口定义,构造方法)运行时常量池

本地方法栈的作用

本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

 

程序计数器的作用

记录线程执行位置 多线程之间的切换

 

 

类加载器

类加载器负责加载 Java 类的字节代码到 Java 虚拟机中。

 

包括启动类加载器(BootStrap ClassLoader), 扩展加载器(Extension ClassLoader),应用加载器(Application ClassLoader)

 

当一个类加载器收到类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载类无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载。

 

 

1包括类加载器

 

根据给定的全限定名类名(如:java.lang.Object)来装载class文件到Runtime data area中的method area

 

2执行引擎(执行class命令)

 

3 Native Interface(本地接口):

与native libraries交互,是其它编程语言交互的接口。

4 Runtime data area(运行时数据区域)

这就是我们常说的JVM的内存。

5 个部分:

程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成;

Java 虚拟机栈(Java Virtual Machine Stacks):用于存储局部变量表、操作数栈、动态链接、方法出口等信息;

本地方法栈(Native Method Stack):与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的;

Java 堆(Java Heap):Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存;

方法区(Methed Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

一、程序计数器

1.程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。

2.为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

3.从上面的介绍中我们知道程序计数器主要有两个作用:

字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。

在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。

注意:程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

二、虚拟机栈

1、与程序计数器一样,Java 虚拟机栈也是线程私有的,它的生命周期和线程相同,描述的是 Java 方法执行的内存模型,每次方法调用的数据都是通过栈传递的。

2、每个方法被执行的时候都会同时创建一个栈帧用于存储(局部变量表、操作栈、动态链接、方法出口)等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

3、栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。

①局部变量表:

1.它一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。并且在Java编译为Class文件时,就已经确定了该方法所需要分配的局部变量表的最大容量。

2.局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)「String是引用类型」,对象引用(reference类型) 和 returnAddress类型(它指向了一条字节码指令的地址)

②操作数栈:

- 与局部变量表一样,均以字长为单位的数组。不过局部变量表用的是索引,操作数栈是弹栈/压栈来访问。操作数栈可理解为java虚拟机栈中的一个用于计算的临时数据存储区。

- 存储的数据与局部变量表一致含int、long、float、double、reference、returnType,操作数栈中byte、short、char压栈前(bipush)会被转为int。

- 数据运算的地方,大多数指令都在操作数栈弹栈运算,然后结果压栈。

- java虚拟机栈是方法调用和执行的空间,每个方法会封装成一个栈帧压入占中。其中里面的操作数栈用于进行运算,当前线程只有当前执行的方法才会在操作数栈中调用指令(可见java虚拟机栈的指令主要取于操作数栈)。

- int类型在-1~5、-128~127、-32768~32767、-2147483648~2147483647范围分别对应的指令是iconst、bipush、sipush、ldc(这个就直接存在常量池了)

————————————————

原文链接:https://blog.csdn.net/qq_28666081/article/details/85269879

③动态连接:

每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接 。

 

 

1.动态链接又称为指向运行时常量池的方法引用。

2.一个方法对应一个栈帧。每一个栈帧中都包含一个指向运行时常量池中该栈帧所属方法的引用。

3.Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用(Symbolic Reference)保存在class文件的常量池里。例如:描述一个方法调用了另外其他的方法时,就是通过常量池中指向方法的符号引用来表示的,动态链接的作用是为了将这些符号引用转换为调用方法的直接引用。

————————————————

原文链接:https://blog.csdn.net/u011069294/article/details/107137927/

 

④方法返回地址:

当一个方法被执行后,有两种方式退出这个方法。

1.当执行引擎遇到任意一个方法返回的字节码指令,这时候可能会有返回值传递给上层的方法调用者,是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法的方式称为正常完成出口。

2.在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是Java虚拟机内部产生的异常,还是代码中使用的字节码指令产生的异常,只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方法的方式称为异常完成出口。一个方法使用异常完成出口的方式退出,是不会给它的上层调用者产生任何返回值的。

3.无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的PC计数器的值就可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中一般不会保存这部分信息。

4、Java 虚拟机栈会出现两种错误:StackOverFlowError 和 OutOfMemoryError。

StackOverFlowError:若 Java 虚拟机栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。

OutOfMemoryError:若 Java 虚拟机堆可以动态扩展,扩展到无法申请足够的内存,并且垃圾回收器也无法提供更多内存的话。就会抛出 OutOfMemoryError 错误。

 

三、本地方法栈

本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

四、堆

1、堆是Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。

2、Java 堆是垃圾收集器管理的主要区域,因此也被称作GC 堆,从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间,默认比例是8:1:1。进一步划分的目的是更好地回收内存,或者更快地分配内存。

3、在JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,最大区别是:元空间并不在JVM中,而是使用本地内存。并且将原来放在方法区的字符串常量池和静态变量都转移到了Java堆中

五、方法区

1、方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。

2、方法区是所有线程共享的内存,在java8以前是放在JVM内存中的,由永久代实现,受JVM内存大小参数的限制,在java8中移除了永久代的内容,方法区由元空间实现,并直接放到了本地内存中,不受JVM参数的限制(当然,如果物理内存被占满了,方法区也会报OOM),不过总的来说分为这两部分:

类元信息:

①、类元信息在类编译期间放入方法区,里面放置了类的基本信息,包括类的版本、字段、方法、接口以及常量池表。

②、常量池表存储了类在编译期间生成的字面量、符号引用,这些信息在类加载完后会被解析到运行时常量池中。

运行时常量池:

①、运行时常量池主要存放在类加载后被解析的字面量与符号引用,但不止这些。

②、运行时常量池具备动态性,可以添加数据,比较多的使用就是String类的intern()方法。

3、为何废弃永久代?

官方:移除永久代是为融合HotSpot JVM与 JRockit VM而做出的努力,因为JRockit没有永久代,不需要配置永久代。

实际:由于永久代内存经常不够用或发生内存泄露,爆出异常java.lang.OutOfMemoryError: PermGen。

 

 

类的实例化顺序

 

比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,他们的执行顺序

先静态、先父后子。

 

先静态:父静态 > 子静态

 

优先级:父类 > 子类 静态代码块 > 非静态代码块 > 构造函数

一个类的实例化过程:

 

父类中的static代码块,当前类的static

顺序执行父类的普通代码块

父类的构造函数

子类普通代码块

子类(当前类)的构造函数,按顺序执行。

子类方法的执行

————————————————

原文链接:https://blog.csdn.net/culous/article/details/105101722

 

StackOverFlowError

1.1、发生的地方:虚拟机栈和本地方法栈

1.2、线程调用生成的栈桢深度超过了虚拟机允许的深度(比如一个方法A内调用了方法B,然后B又调用C...这样调用了1万层,就像你要去地狱十八层,但是虚拟机只提供了到九层的梯子),不过现在大多数虚拟机栈的调动深度都是可以动态扩展的,所以这个问题出现的概率非常的低。

————————————————

原文链接:https://blog.csdn.net/qq125281823/article/details/104491377/

OutOfMemoryError

    1. 基本上虚拟机的运行时数据区域(堆,栈,方法区)都会发生
    2. 2.1、内存不够,发生了内存溢出
    3. 3.1、申请更多内存,调整虚拟机的启动参数 -Xms1500m -Xmx1500m -XX:PermSize=125M -XX:MaxPermSize=256M

 

 

JDK、JRE、JVM关系?

 

Java中的异常有哪几类?分别怎么使用?

 

Java 8的内存分代改进

 

谈谈你对jvm的理解 java8和之前有什么变化

什么是oom 什么是栈溢出 怎么分析

Jvm的常用调优参数有哪些

内存快照如何抓取 怎么分析dump文件

类加载器你的认识

GC是什么?为什么要GC

沙箱安全机制

垃圾回收的优点和原理。

垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?

Java 中都有哪些引用类型?

怎么判断对象是否可以被回收?

在Java中,对象什么时候可以被垃圾回收

jvm中一次完整的GC流程(从ygc到fgc)是怎样的,重点讲讲对象如何晋升到老年代等

JVM中的永久代中会发生垃圾回收吗

说一下 JVM 有哪些垃圾回收算法?

说一下 JVM 有哪些垃圾回收器?

详细介绍一下 CMS 垃圾回收器?

新生代垃圾回收器和老年代垃圾回收器都有哪些?有什么区别?

简述分代垃圾回收器是怎么工作的?

简述java内存分配与回收策率以及Minor GC和Major GC

描述一下JVM加载Class文件的原理机制

说一下类装载的执行过程?

什么是双亲委派模型?

说一下 JVM 调优的工具?

常用的 JVM 调优的参数都有哪些?

  •  
  • 数据库

Ppt+mysql高级+面试题

 

Mysql的主要配置文件有哪些?

二进制日志文件 log-bin

主要用于主从复制

 

错误日志文件

启动,关闭信息,严重的警告,错误信息(有哪些?) 默认关闭

 

 

查询日志文件

 

数据文件

Frm文件存放的是表框架 myd:数据 myi:索引

 

如何配置

 

Mysql存储引擎有哪些?

Myisam和inodb的区别

性能下降的原因有哪些?

Sql写的烂 索引失效

关联太多表 服务器调优以及各个参数设置

 

 

Sql执行加载顺序

 

 

 

 

手写:

Groupby orderby

Having orderby

Limit

用法

 

机器读:

 

From

On

Join

Where

Group by

Having

Select

Order by

Limit

 

7种join

索引是什么?

高效获取数据的一种数据结构

类似字典

排好序的快速查找数据结构

会影响到where后面的查找以及order后面的排序

 

在数据以外

数据库还维护了满足特定算法的

数据结构

这些结构以引用指向数据

B树 b+树

索引优势与劣势

索引分类

 

单值索引

唯一索引

复合索引

基本语法

 

创建索引的语句

Create unique(唯一索引)index 名称 on 表名(字段)

 

索引结构

 

Mysql锁机制

索引失效 行锁变表锁   vachar类型不加隐号

 

间隙锁的危害

 

当我们使用范围条件而不是相等条件时

并请求共享或排他锁时 innodb会将符合条件的已有数据的索引项加

锁,对于在范围内并不存在的记录也会对这个间隙加锁,

如何锁定一行 select。。。。。  for update

 

 

 

  • Spring系列

 

Aop怎么实现的?

如何实现代码增强?

静态代理?

动态代理?

日志监听

权限控制

事务管理

非功能代码和功能代码解耦

Springioc源码

加载配置文件--》解析

--》bean定义对象--》实例化--》应用

使用map存储bean

Ioc container(ioc容器)

 

beanFactory是获取(访问bean容器)bean实例的途径

Bean定义:xml方式 注解方式

将bean定义放入到容器

在容器里面叫beandefinition

实际上是bean的定义

实例化时需要依赖的当前属性值

 

Springboot里面的配置文件

Properties 和yaml文件定义的属性值

甚至json定义的属性

Spring 提供了一个统一的接口规范

用于各种方式的读取

Beandefinitionreader 读取器

实例化环节

为什么不直接用new 或者反射(不是直接反射啥的)

间接反射

反射性能低 是在上10万以后性能才低

Bean的创建(生命周期)

反射创建之前

配置文件里面---存在占位符

占位符也会被加载进去ioc容器

那么什么是否占位符会被真实的值取代呢

需要用到一个postprocessor 后置处理器

扩展功能实现

(最杰出的两个代表是beanFactorypostprocessor

和beanPostprocessar)

这两个针对的操作对象是不用的

beanFactorypostprocessor 针对的beanFactory

它将占位符替换为实际值

beanPostprocessar 针对是bean

反射创建对象的方法

Constructor ctor = Clazz.getDecareConstructor();

Object obj = Ctor.newinstance();

//无参创建对象

Class c=Class.forName("...");

Object o=c.newInstance();     //本质获取类的无参构造器,并且有访问权限

 

//有参创建对象

Constructor constructor=c.getDeclareConstructor(String.class);

Object o=c.newInstance("str");

 

创建对象包括两部分--实例化(堆中开辟内存空间) 初始化 1填充属性 2 初始化方法执行 告诉别人初始化成功

Spring中包含的对象 普通对象(用户自定义)和容器对象(容器自己使用的)

如果想在用户自定义的对象中使用容器对象需要用到aware接口

什么用jdk(有接口) 什么时候用cglib(没有接口)

 

 

观察者模式

Spring生命周期有哪些步骤?、

什么是beanDefinition?他为什么非常重要

什么是bean的后置处理器?

什么是bean工厂的后置处理器

什么是beanfactory 它与applicationContext有什么区别

什么是factorybean 它与beanfactory的区别

Import bean component 注解的区别

什么是importbeandefinitionregistrar

他的作用是什么?

Spring的理解

管理对象--》bean

是否所有的bean都是对象 是的

对象不一定是bean

 

 

 

 

 

 

 

 

Springaop源码

 

 

 

 

 

 

  • Nginx

反向代理 负载均衡 动静分离

正向代理:

代理服务器

在我们的浏览器配置代理服务器

通过代理服务器访问互联网

用户访问网址,通过代理服务器,反向路由到我们的真实服务器上。

Nginx安装:

解压 tar -xvf  

执行。./configure

 make & make install 编译安装

查看版本号 pcre-config -version

版本号8以上

解压安装

Yum安装

存在防火墙---需要在防火墙添加开放的端口

查看防火墙开放的端口

Firewall-cmd --list-all

 

设置开放的端口命令

 

Nginx常用命令有哪些?

必须进入nginx目录才有效

进入、usr/local/nginx/sbin目录

1 查看nginx版本号

./nginx -v

版本号1.12.2

停止命令 ./nginx -s stop

启动命令 ./nginx

重加载nginx 配置文件

./nginx -s reload

Nginx配置文件

配置文件位置:conf文件

Nginx.conf文件

 

 

 

三部分组成

  • 全局块

从配置文件开始到events之间内容

整体运行的配置指令,主要包括

用户信息 日志存放路径 worker process

数工作进行数(越大支持的并发越大)

进程pid

 

 

 

二、events

主要影响用户与服务器之间的网络的连接

比如work-connections 1024

最大连接数

 

 

三http全局块

  http块:连接超时时间 日志定义

和server块

包含全局server

监听的端口号 80

Server name名字 localhost

和location块

 

反向代理配置:

修改servername为端口

在location 里面修改转发路径配置

Proxy-pass http:127.0.0.1:8000

根据不同路径跳转到不同的服务器

/eu--->8080

/eo--->8081

 

正则表达式

Location飄号/eu/

{

Proxy-pass

}

Location飄号/eo/

{

Proxy-pass

}

负载均衡:

在http全局配置里面

配置服务ip和权重

在location里面配置路径和连接超时时间

第一种 轮询

第二种 权重

权重越高 被分配的流量越多

第三种 ip hash 方式

根据ip进行hash 每个ip访问同一个服务器

可以解决session问题

Fair方式 根据服务器响应时间进行分配

谁先响应 谁分配

动静分离

什么动静分离

把动态请求和静态请求分开

得到图片 得到html

Nginx就将请求转发到静态资源服务器

把静态文件独立成单独的域名

放在独立的服务器上

另一种是混合发布通过nginx分开

==

通过location 指定不同的后缀名 实现不同的请求转发

通过expire参数设置

可以使浏览器缓存过期时间

减少与服务器的请求和流量

具体是给一个资源假定一个过期时间

 

也就是说无需去服务端验证,直接浏览器自己验证

是否过期====适合不经常变动的资源。

设置3天 3天内比对服务器最后变更时间是否变化,无变化返回304 有修改返回200

--

Location /www/{

Root 路径

Index index.html

}

高可用

一主一次

利用keepalived

来实现主从

--

虚拟ip 当主挂掉以后,将流量(ip)绑定给从

 

 

 

 

 

1.nginx常用命令

启动nginx  ./sbin/nginx

停止nginx ./sbin/nginx -s stop    ./sbin/nginx -s quit

重载配置  ./sbin/nginx -s reload(平滑重启)  service nginx reload 

重载指定配置文件 ./sbin/nginx -c /usr/local/nginx/conf/nginx.conf

查看nginx版本 ./sbin/nginx -v

检查配置文件是否正确 ./sbin/nginx -t

显示帮助信息 ./sbin/nginx -h

————————————————

原文链接:https://blog.csdn.net/xal0610/article/details/79531692

3.nginx是如何实现高并发的

一个主进程,多个工作进程,每个工作进程可以处理多个请求

每进来一个request,会有一个worker进程去处理。但不是全程的处理,处理到可能发生阻塞的地方,比如向上游(后端)服务器转发request,并等待请求返回。那么,这个处理的worker继续处理其他请求,而一旦上游服务器返回了,就会触发这个事件,worker才会来接手,这个request才会接着往下走。

由于web server的工作性质决定了每个request的大部份生命都是在网络传输中,实际上花费在server机器上的时间片不多。这是几个进程就解决高并发的秘密所在。即@skoo所说的webserver刚好属于网络io密集型应用,不算是计算密集型。

 

4.nginx功能

作为http server(代替apache,对PHP需要FastCGI处理器支持)

反向代理服务器

实现负载均衡

虚拟主机

FastCGI:Nginx本身不支持PHP等语言,但是它可以通过FastCGI来将请求扔给某些语言或框架处理

————————————————

原文链接:https://blog.csdn.net/xal0610/article/details/79531692

 

6.nignx配置

worker_processes  8;     工作进程个数

worker_connections  65535;  每个工作进程能并发处理(发起)的最大连接数(包含所有连接数)

error_log         /data/logs/nginx/error.log;  错误日志打印地址

access_log      /data/logs/nginx/access.log  进入日志打印地址

log_format  main  '$remote_addr"$request" ''$status $upstream_addr "$request_time"'; 进入日志格式

 

fastcgi_connect_timeout=300; #连接到后端fastcgi超时时间

fastcgi_send_timeout=300; #向fastcgi请求超时时间(这个指定值已经完成两次握手后向fastcgi传送请求的超时时间)

fastcgi_rend_timeout=300; #接收fastcgi应答超时时间,同理也是2次握手后

fastcgi_buffer_size=64k; #读取fastcgi应答第一部分需要多大缓冲区,该值表示使用1个64kb的缓冲区读取应答第一部分(应答头),可以设置为fastcgi_buffers选项缓冲区大小

fastcgi_buffers 4 64k;#指定本地需要多少和多大的缓冲区来缓冲fastcgi应答请求,假设一个php或java脚本所产生页面大小为256kb,那么会为其分配4个64kb的缓冲来缓存

fastcgi_cache TEST;#开启fastcgi缓存并为其指定为TEST名称,降低cpu负载,防止502错误发生

 

listen       80;                                            监听端口

server_name  rrc.test.jiedaibao.com;       允许域名

root  /data/release/rrc/web;                    项目根目录

index  index.php index.html index.htm;  访问根文件

 

7.nginx和apache的区别

轻量级,同样起web 服务,比apache 占用更少的内存及资源 

抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能

高度模块化的设计,编写模块相对简单 

最核心的区别在于apache是同步多进程模型,一个连接对应一个进程;nginx是异步的,多个连接(万级别)可以对应一个进程 

 

————————————————

原文链接:https://blog.csdn.net/xal0610/article/details/79531692

 

  • Redis

场景 业务

性能 原理 bug

 

1 Redis是什么

内存的 kv的 工作线程是单线程的

  1. x版本以后工作线程也是单线程

Io threads io知识点

持久化 同步 集群---》

技术选型

 

 

多路复用器 linux epoll

单线程弊端

只能利用1个cpu 不能发挥硬件

增加的是io线程 工作线程还是单线程

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • Linux shell

 

  • Mq
  • 分布式事务

二阶段提交

Xa规范是什么?

定义了

资源管理器 事务管理器

Jta是什么?

 是实现了xa的java规范

两阶段提交 记录日志重试

 

Atomikos

方案有哪些

Seta mq

修复数据

大部分没有实时的解决

大部分是靠修复数据

通过异常-----

全局事务

存在全局事务id

一个全局事务上面存在3个分支事务

具体操作

自定义注解

在方法上加上注解

如果有注解就创建全局事务imap

所有分支事务都会注册到全局事务上面

调用链接进行,出现有需要回滚的事务

需要通知各个系统 各个系统在提交前wait

基于netty

 

分布式事务本身来说是单个服务在调用另一个服务时

Spring事务因为绑定数据库连接的关系

只会对当前的数据库连接生效,那么会造成数据不同步的问题

我们的分布式事务管理是通过一个全局事务管理器来管理分布式事务的

具体来说就是,在一个服务里面,当他需要用到事务并且需要在调用另一个服务的同时保证数据一致性

那么他就需要先在我们的全局事务管理器里面进行注册

全局事务里面通过一个map进行存储此次注册的信息,说明他开启了一个全局事务(全局事务id),并且此次事务也会作为一个子事务进行存储,每调用一个系统都会在全局事务管理器里面创建一个子事务,记录所有的子事务的状态是commit 还是rollback,并且检查所有的子事务的状态(每次添加时),当出现rallback时 通知所有系统进行回滚操作。

难点1 拿到事务的控制权

事务控制权是通过利用aop

在建立连接时 也就是getconnection时进行aop

进行包装

当spring获取DataSource连接时,得到的连接对象

是我们自己实现的collection对象

那么我们就能拿到commit权限

 

难点2 通知所有系统进行提交或者回滚

 

难点3 开启全局事务

使用的自定义注解的切面

开启完以后通过执行

Point.proceed

通过查看是否存在异常来进行最后判定

子事务是提交还是回滚

 

怎么知道最后已经执行结束了呢

怎么通知

每一个服务本地

都有一个本地事务对象

当接收到事务管理器的通知以后,会查找本地所有的

事务对象(利用全局事务的id去查找)

在向事务管理器注册分支事务时会创建一个当前的

分支事务对象,便于管理当前的所有的相关事务

确定完以后所有的分支事务的状态以后

唤醒所有的分支

--

在休眠时 需要另起一个线程进行休眠 不然的话会造成死循环,

 

 

 

 

 

 

 

Nettyclient客户端

Netty实现的发送和接收

 

redis

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值