-
ArrayList底层是数组,LinkedList底层是链表;
-
ArrayList的查询效率更高,LinkedList的增删操作更快,因为LinkedList需要移动指针从前往后一次查找;
-
ArrayList自由性较低,因为它需要手动的设置固定大小的容量,但是它的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用;而LinkedList自由性较高,能够动态的随数据量的变化而变化,但是它不便于使用;
-
LinkedList的占据的存储空间更大,他不仅存储自己的结点,还会存储结点指针信息。
- HashMap 是非线程安全的,HashTable 是线程安全的,因为 HashTable 内部的方法基本都经过synchronized/ˈsɪŋkrənaɪzd/修饰;
- 因为线程安全的问题,HashMap 要比 HashTable 效率高一点;
- HashMap 可以存储 null 的 key 和value,但 null 作为键只能有一个,null 作为值可以有多个;而HashTable 不允许有 null 键和 null值,否则会抛出 NullPointerException;
- PS: HashMap逐渐被线程安全的 ConcurrentHashMap取代,想使用单线程的话,ConcurrentHashMap用的比较多;
- PS:HashTable 也基本被淘汰,在代码中很少再使用它;
- 1.HashSet是基于HashMap的;
- HashMap实现了Map接口,实现Set接口;
- HashMap存储键值对,HashSet仅存储对象;
- 添加元素:HashMap调用put()向map中添加元素 ,HashSet调用add()方法向Set中添加元素;
- 计算hashcode值:HashMap使用键(Key)计算Hashcode,HashSet使用成员对象来计算hashcode值;
- HashMap相对于HashSet较快,因为它是使用唯一的键获取对象,HashSet较HashMap来说比较慢。
并发是指两个或多个事件在同一时间间隔发生,在同一实体上;
并行是指两个或者多个事件在同一时刻发生,在不同实体上。
举个例子:并发就像两队在排队等一个咖啡机,是穿插着交替使用的;并行是两队排队等两个咖啡机,两队互不影响;
并发:并发在一台处理器上“同时”处理多个任务。(看起来像是同时处理的)
并行:并行在多台处理器上同时处理多个任务。
进程:当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。
线程:一个进程又是由多个线程所组成的,线程是程序中的一个执行流,每个线程都有自己的专有寄存器,但代码区是共享的;
单线程是在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行;
多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务;
单线程可能会造成阻塞,
多线程可能会造成死锁;
多线程的好处:可以提高CPU的利用率;
多线程的缺点:
- 线程需要占用内存,线程越多占用内存也越多;线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;
- 线程太多会导致控制太复杂,最终可能造成很多Bug;
死锁: 是指两个或两个以上的进程(线程)在运行过程中因争夺或者等待对方手里的资源而造成的一种僵局,若无外力作用,这些进程(线程)都将无法向前推进 ,这时就形成了死锁。
比如一扇门,你要出我要进,你在等我让,我在等你让,这时就陷入了死循环,就形成了死锁。
8.什么是线程死锁?如何避免死锁?
- 互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源
- 请求和保持条件:进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事请求阻塞,但又对自己获得的资源保持不放
- 不可剥夺条件:是指进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放
- 循环等待条件:是指进程发生死锁后,必然存在一个进程–资源之间的环形链
1.预防死锁:通过设置一些限制条件,去破坏产生死锁必要条件的一个或多个来预防死锁
2.避免死锁:在资源分配过程中,使用某种方法避免系统进入不安全的状态,从而避免发生死锁
3.检测死锁:允许死锁的发生,但是通过系统的检测之后,采取一些措施,将死锁清除掉
4.解除死锁:当检测出死锁后,便采取适当措施将进程从死锁状态中解脱出来
新建(New)、就绪(Runnable)、运行(Running)、阻塞 (Blocked)和死亡(Dead)5 种状态
1.新建状态当程序使用 new 关键字创建了一个线程之后,该线程就处于新建状态,此时仅由 JVM 为其分配 内存,并初始化其成员变量的值
2. 就绪状态当线程对象调用了 start()方法之后,该线程处于就绪状态。Java 虚拟机会为其创建方法调用栈和 程序计数器,等待调度运行。
3. 运行状态如果处于就绪状态的线程获得了 CPU,开始执行 run()方法的线程执行体,则该线程处于运行状态。
4. 阻塞状态指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu 时间片,暂时停止运行。 直到线程进入可运行状态,才有机会再次获得 cpu时间片转到运行状态。阻塞的情况分三种:
等待阻塞(o.wait->等待对列):运行的线程执行 o.wait()方法,JVM 会把该线程放入等待队列中。
同步阻塞(lock->锁池):运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线 程放入锁池中。
其他阻塞(sleep/join):运行的线程执行 Thread.sleep(long ms)或 t.join()方法,或者发出了 I/O 请求时, JVM 会把该线程置为阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入可运行状态。
线程会以下面三种方式结束,结束后就是死亡状态。
正常结束:run()或 call()方法执行完成,线程正常结束。
异常结束:线程抛出一个未捕获的 Exception 或 Error。
调用 stop:直接调用该线程的 stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用。
synchronized/ˈsɪŋkrənaɪzd/(ps读作:thin可若乃子的)是一种同步锁,可以保证被它修饰的方法或者代码块在任一时刻只有一个线程在执行。
volatile /ˈvɒlətaɪl/(ps读作:窝乐泰奥)是轻量级的synchronized;
-
volatile关键字主要用于解决变量在多个线程之间的可见性,并不能保证原子性;不会造成线程阻塞;
-
synchronized关键字解决的事多个线程访问资源的同步性,意思就是用synchronized锁定之后只能当前线程可以使用,别的线程不能在使用了;保证变量的修改可见性和原子性;会造成线程阻塞
附:synchronized用法浅述:
修饰实例方法:给当前实例对象加锁,进入同步代码块之前需要获得当前实例对象的锁;
修饰静态方法:给当前类加锁,会所用于类的所有实例对象,进入同步代码块之前要获得当前class的锁。
修饰代码块:指定加锁对象,对给定对象/类加锁。
并发编程的三个重要特性是原子性、有序性和可见性。
- 原子性:要么都做,要么都不做。
- 可见性:当一个线程对共享变量进行了修改,那么另外的线程可以立即看到修改后的新值。
- 有序性:程序代码在执行过程中先后顺序,由于 Java 在编译器以及运行期的优化,导致了代码的执行顺序未必就是开发者编写代码的顺序。
Lambda 表达式:(parameters) -> expression或(parameters) ->{statements; }
ps:还有别的,就列举了一个;
11.说说MyISAM和InnoDB的区别?MyISAM:5.5版之前的MySQL的默认数据库引擎;
InnoDB:现为MySQL的默认存储引擎。
ps:数据库引擎:用于存储、处理和保护数据的核心服务。
区别:- InnoDB支持事务,MyISAM不支持。对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务。
- InnoDB支持外键,而MyISAM不支持。
- InnoDB是聚集索引,MyISAM是非聚集索引,都使用B+Tree作为索引结构,但MyISAM索引和数据文件是分离的,索引保存的是数据文件的指针,主键索引和辅助索引是独立的。
也就是说:InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。 - InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描;而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
- InnoDB支持表、行(默认)级锁,而MyISAM支持表级锁。InnoDB的行锁是实现在索引上的,而不是锁在物理行记录上。潜台词是,如果访问没有命中索引,也无法使用行锁,将要退化为表锁。
- 是否要支持事务,如果要请选择innodb,如果不需要可以考虑MyISAM;
- 如果表中绝大多数都只是读查询,可以考虑MyISAM,如果既有读也有写,请使用InnoDB。
- 系统奔溃后,MyISAM恢复起来更困难,能否接受;
- MySQL5.5版本开始Innodb已经成为Mysql的默认引擎(之前是MyISAM),说明其优势是有目共睹的,如果你不知道用什么,那就用InnoDB,至少不会差。
- A-原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
- C-一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
- I-隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交、读提交、可重复读和串行化。
- D-持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
跟事务的隔离级别是有关系的,
- 脏读: 当两个事务选择同一行进行更新时,最后更新就会覆盖其他事务所做的更新,即:事务A读到了事务B已经修改但是还没有提交的数据。
- 不可重复读: 一个事务在读取某些数据成功后,没有提交事务,在下一个时间再去读取之前已经读过的数据,发现数据已经变了或者已经删除了
- 幻读: 事务A读取到了事务B提交的新增数据,不符合隔离性
不可重复读和幻读区别:
不可重复读的重点是修改,⽐如多次读取⼀条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除,⽐如多次读取⼀条记录发现记录增多或减少了。
事务隔离级别:
| 隔离级别 | 脏读 | 不可重复度读 | 幻读 |
|---|---|---|---|
| 读未提交 | 可能 | 可能 | 可能 |
| 读已提交 | 不可能 | 可能 | 可能 |
| 可重复读 | 不可能 | 不可能 | 可能 |
| 可串行化 | 不可能 | 不可能 | 不可能 |
查看当前数据库的事务隔离级别:show variables like ‘tx_isolation’;
设置当前的数据库的事务隔离级别:set session transaction isolation level read uncommitted;
mysql默认是可重复读级别.
Redis:即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库。
即,数据库
1、Redis 是基于内存的数据库,本身数据就存在于内存里,也就没有了磁盘数据库那种将数据从磁盘读取到内存里这个过程,节省了I/O 操作方面的开销;
2、redis是单线程的,而多线程在执行过程中需要进行 CPU 的上下文切换,这个操作比较耗时。
3、I/O 多路复用模型。
IoC :控制反转(Inversion of Control),对象的创建、实例化、管理的权力交给IoC容器处理,需要使用的对象从IoC容器中获取。IoC解决了对象之间的耦合问题
ps:IoC与DI的区别
DI:依赖注入(Dependancy Injection):是指程序运行过程中,如果需要调用另一个对象协助时,无须在代码中创建,而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求,完全支持对POJO之间依赖关系的管理。
ps:POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans
区别:IoC容器负责创建、实例化、管理对象,DI负责进行对象之间依赖的注入
AOP:面向切面编程(Aspect oriented Programing),AOP是一种横向抽取机制,将横切逻辑代码和业务逻辑代码分离,能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
16.SpringMVC 工作原理了解吗?第一步:用户发起请求到前端控制器(DispatcherServlet)
第二步:前端控制器请求处理器映射器(HandlerMappering)去查找处理器(Handle):通过xml配置或者注解进行查找
第三步:找到以后处理器映射器(HandlerMappering)像前端控制器返回执行链(HandlerExecutionChain)
第四步:前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)去执行处理器(Handler)
第五步:处理器适配器去执行Handler
第六步:Handler执行完给处理器适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回ModelAndView
第八步:前端控制器请求视图解析器(ViewResolver)去进行视图解析
第九步:视图解析器像前端控制器返回View
第十步:前端控制器对视图进行渲染
第十一步:前端控制器向用户响应结果
17.@SpringBootApplication由哪几个注解组成?@SpringBootConfiguration:等同于@Configuration,带有spring的标志,是属于spring的一个配置类;
@EnableAutoConfiguration:开启自动配置;
@ComponentScan:开启包扫描.。
引用计数算法 和 可达性分析算法,目前常用的就是可达性分析算法;
-
程序计数算法:对一个对象添加一个引用的计数器,当该对象被引用依次那么计数器+1,如果引用被释放,那么计数器-1,这样根据对象最终引用次数为0时,将该对象回收;
-
可达性分析算法:首先找到一个GC root点,通过该点作为起始点,无法与起始点相连的对象都是不可达的,需要进行回收,反之就存活;
ps:能够作为GC Root 的对象可以为:
1、方法区中常量引用的对象、
2、方法区中静态属性引用的对象;
3、虚拟栈帧中引用的对象;
4、本地方法栈引用的对象;
Spring Boot 最主要是不用 XML 配置,可以用 Java 来配置 bean,省去了许多配置文件。
重点:自动配置。
Spring Boot 的开启注解是:@SpringBootapplication
其中最最核心的就是这个 @EnableAutoConfiguration 注解了,它能根据类路径下的 jar 包,动态加载配置和注入bean。
如果是传统的项目,我们要自己手动写一大堆的配置,而且还不灵活,有了这个启动器,我们就可以做到简单集成。
ps:本人啥也不懂,只能浅谈
1.存储引擎
2.缓存
3.执行计划,sql运行时间明细
4.分库分表:
引入分库分表机制,通过分库,可以降低单个服务器节省的一个IO压力,通过分表,可以去降低单表数据量,从而去提升sql查询效率;针对热点数据,可以引入更高效的分布数据库,像redis mongoDB等,它们可以很好地缓解Mysql的访问压力,同时还能医生数据的检索性能。
5.读写分离,主从复制
6.碎片整理
7.备份
mybatis中设计了二级缓存这样一个机制来提升数据的检索效率,以避免每次检索数据的时候都去查询数据库。
一级缓存是SQLSession级别的一个缓存,也叫本地缓存,因为每一个用户在执行查询操作的时候都需要SQLSession来执行,为了避免每一次都去查询数据库,Mybatis把查询出来数据缓存到SQLSession的本地缓存里面,后续的SQL如果在命中缓存的情况下就可以直接从本地缓存去读取数据;如果想要实现跨SQLSession级别的一个缓存,一级缓存无法做到,此时引入二级缓存,当多个用户在查询数据的时候只要有任何一个SQLSession拿到数据就会放到二级缓存中,那么其他的SQLSession就可以直接从二级缓存里面去加载数据。
一级缓存在SQLSession里面会持有一个Executor,每一个Executor里面会有一个叫localCache的对象,当用户发起查询的时候,Mybatis会根据执行语句,在localCache里面去查找,如果命中了就直接把这个数据返回,如果没有命中就再去数据库里面查询出来,在写入到localCache中,所以一级缓存的生命周期是SQLSession。
PS:在多个SQLSession或者分布式环境下可能会因为一级缓存导致脏读;
二级缓存是在原来的Executor上做了一个装饰,引入了CachingExecutor这一装饰器,在进入一级缓存的查询之前回西安通过CachinExecutor进行耳机缓存的查询,开启二级缓存之后会被多个SQLSession共享,所以他是一个全局的缓存,所以此时查询流程就变成了先查二级缓存,再查一级缓存,然后再查数据库。
二级缓存比起一级缓存来,多了一个缓存数据的共享,同时缓存粒度也能控制namespace一个级别,并且还能通过Cache这一接来实现不同缓存实现类的一个组合,对Cache的可控性更高了。



