java初级面试题

并发

创建线程的方式

创建线程总共有四种方式

1、继承Thread类

重写run方法(执行的代码块)

使用方法:创建继承后的对象,调用start方法

2、实现runnable接口

重新run方法

使用方法:创建一个Thread对象 后创建实现ruNNABLED的对象放入Thread中调用start方法

3、实现Callable接口(传递泛型跟重新的方法有关联)

重写Call方法

使用方法:创建实现的对象,创建FutureTask(对象),创建Thread类(融入FutureTask对象)调用start方法,调用FutureTask对象的get方法获取执行结构

4、线程池创建线程

实现runnable 重写run方法

//重写run
psvm
//创建线程池对象
我们只需要提交任务即可
Executor threadPool=Executors.newFiexThreadPool(3);
//提交任务后他自动执行
threadPool.submit(new MyExecutors());

//关闭线程池
threadPool.shutDown();

runnable和callable创建线程有什么区别 

1、runnable接口的run方法没有返回值

2、callable有返回值是个泛型,和Future、FutureTest配合可以获取异步执行操作

3、callable接口的call方法允许抛出异常而runnable的run方法异常只能再内部消化trycath

run和start的区别?

start():用来启动线程,通过该线程调用run方法执行run的代码,start只能调用一次

run();封装了被线程执行的代码可以被多次调用

java中 wait和sleep的区别(等待和睡觉的区别)

共同点

wait、wait(long)、sleep(long)都可以让当前线程暂时放弃cpu的使用权进入堵塞

不同点

1、归属方法不同

sleep是Thread的静态方法

而wait是Object类的成员方法每个对象都有

2、醒来时机不同

执行sleep(long)和wait(long)的线程都会在等待相应毫秒后醒来

wait(long)和wait()都可以被notify唤醒,wait()不唤醒会一直等待下去

他们都可以被打断唤醒

3、锁特性不同(重点)

wait必须先获取wait对象的锁,sleep不用

wait方法执行后会释放对象锁,允许其他线程获取该对象锁(我放弃cpu但你们还可以用)

sleep方法如果还还在synchronized代码块中执行,并不会释放对象锁(我放弃CPU但你们不能用)

synchronized关键字底层原理

lock是一个对象锁
synchronized(lock){
    if(tickerNum<=0)
    return;
    sout();
    tickerNum--;
}

对象锁采用互斥方方式同一时刻至多一个线程持有对象锁,其他线程再想获取这个锁就会阻塞

底层是monitor(监视器由JVM提供,C++实现)

当一个线程来访问时进行上锁,对执行的代码块执行完后解锁,然后再解锁,为什么有2次解锁,因为如果我们写的代码块抛出了异常,第一次的解锁将失效,如果没有第二个解锁如果发生异常,当前线程不释放,则其他线程将无法进入加第二个是为了以防万一。 

当有线程访问时先看owner是否为空,如果是则将线程连入、之后若有线程访问则依然查询owner是否被其他线程占用,此时如果是则进入EntryList进入阻塞态,注意阻塞的线程没有先后顺序,谁先抢到owner就是谁的 waitSet当一个线程调用wait方法,他会吧等待的线程放入waitset中

synchronized关键字底层原理-进阶

Moniter实现的锁属于重量锁,你了解锁升级吗

Moniter属于重量锁,里面涉及了用户态和内核态的切换,进程上下文的切换,成本较高,性能比较低。

在jdk1.6时引入了偏向锁和轻量级锁,它们是为了解决在没有多线程或者基本没有竞争的场景下因使用原本的锁机制带来了性能开销问题。

CAS

CAS是一种乐观锁的思想,在无锁情况下保证线程操作共享数据的原子性

在JUC中很多类都实现了CAS 

A:V会进行比对如果A=V才会进行赋值操作,如果A≠V则会进行自旋如下

自旋:重新读一份新值放入再AV对比如果不同继续(死循环)自旋

自旋的优势:这里的AV对比并没有加锁,所以并没有阻塞 

CAS底层实现

CAS底层依赖于一个Unsafe类来直接调用操作系统的CAS指令

乐观锁和悲观锁的区别

CAS基于乐观锁:最乐观的估计,不怕别的线程来修改共享变量,就算改了也没关系自旋一下

synchronized基于悲观锁,最悲观的估计,得防着其他线程改变共享变量,我上了锁都别想改,我改完了开锁了你们才有机会

死锁产生的条件

死锁:一个线程需要同时获取多把锁,这时就容易产生死锁

如何死锁诊断

jdk自带工具jps和jstack

jps:输出JVM中所有的进程状态信息

jstack:查看java线程内线程堆栈信息

jdk自带的可视化工具

jconsole

用于对jvm内存,线程、类监控,是基于一个jmx的gui

bin目录下 jconsole.exe

visualVM故障处理工具

能够监控线程,内存情况,查看方法的CPU时间和内存中的对象,已被GC的对象,反向查看分配的堆栈打开方式:java安装目录 bin目录下 jvisualvm.exe

导致并发程序出现问题的根本原因是什么

(JAVA程序中怎么保证多线程的执行安全)

导致并发程序出现问题的根本原因是什么(Java程序中怎么保证多线程的执行安全)

java的三大特性

原子性:一个线程在CPU中操作是不可暂停的,不可以中断,要不执行完成,要不不执行

解决方法加锁1、synchroized同步加锁、JUC里的lock锁

可见性:让一个线程对共享变量的修改对另一个线程可见加volatile

有序性

集合

ArrayList和LinkList的区别

数组和链表

高并发中的集合

java中的集合有三代

第一类集合他默认就是线程安全的比如vector、hashtable、里面全是synchronized修饰方法,但效率低下

第二类、后来jdk作者发现大多数情况下是用不到多线程的所以就有了第二类 arraylist和linklist线程非安全、线程不安全但性能好、但需要线程安全时,用collection.cynchronizedlist(list)、collection.cynchronizedmap(map)底层使用的是sychronized的代码块锁、但他没有使用新的JUC性能也是偏低的、但之后sychronized的锁也得到了升级、虽然变得稍微灵活但性能还是不够看的

第三类线程安全集合类

cas自旋锁、底层大多是使用aqs总之在大多数情况下是优于二代 

聊一下java集合类

集合体系、常用类、接口、实现类

知道的高并发集合类JUC集合增强内容

HashMap为什么用红黑树

jdk1.8之后java对其进行改进,当链表长度大于8时对后面的数据存入红黑树中

hashmap原本的桶数组后面是链表、当删除时到了6个则会退化成链表

为什么要用呢?

如果用链表查询数据找第四个数据顺序查重o(n)

如果用树型查找(默认使用二分查找)o(log2n)加快了速度

在jdk1.7时使用头插法,1.8时使用尾插法

为什么不使用AVL而使用红黑树做hashmap

前者高度平衡回旋次数多

引入红黑树因为二叉查找树在某些情况下会变成线性结构

hashMap和hastable区别

1、hashmap是线程非安全、hashtable是线程安全、hashtable底层是synchronized修饰。现在一般不用可以用第三代线程安全锁ConcurrentHashMap

2、因为线程安全问题HashMap比HashTable效率更高

3、HashMap中,null可以作为键值,这样的键值只有一个可以一个或多个键值对应的值为null\

但HashTable只要put进的一个键值为null则会抛出Nullpoint

4、扩容机制不同如果不指定hashtable则默认初始长度为11之后每次扩容容量变为2n+1 

hashmap初始化为6之后每次扩充长度扩充为2的幂次方大小用的便是hashmap中tableSizeFor()

5、jdk1.8之后将原有的链表散链(数组+链表)变成了数组+红黑树

HashMap的默认加载因子为什么是0.75

hashmap初始化长度为16,负载因子默认为0.75 hashmap容纳量=长度*负载因子  也就是说长度越大能容纳的键值对就越多

而默认的0.75是空间和时间效率平衡下的选择

在jdk1.7中作何在负载因子默认为0.75下有一行注释意为作为一般规则默认的负载因子(0.75)在时间上和空间上都有很好的折中。较高的值会降低空间开销,但提高了查找成本(put和get)

如果对内存剩余空间很多而时间效率很高可以降低负载因子

如果空间紧张而对效率不太高要求则可以增加负载因子

hashmap中存储key的索引是怎么计算的

首先根据key值算出hashcode值,然后根据code值算出hash值,再通过hash&(lengh-1)计算最终位置

在jdk1.7和jdk1.8中两者是区别的在第一步中二者相同是通过key值获取code值而区别就在于第二步

hashmap中put方法的流程

1、首先根据key计算hash值找到该元素在数组的存储下标

2、如果数组是空的,则调用resize进行初始化

3、如果没有哈希冲突之间放在对应的数组下标中

4、如果冲突了存在就覆盖

5、如果冲突后不存在且是红黑树直接挂载

6、人工冲突后是链表且不存在先判断是否大于8

HashMap一般指定什么为key

一般用integer、String作为hashmap的key

因为字符串是不可改变的,所以当他创建时hashcode就被缓存、不需要重新计算

在hashmap中因为获取对象都要使用equals()和hashcode()所以这些类已经将hashcode和equals重重写

hashcode为什么线程不安全

1.7头插法会引起闭环参考

并发下HashMap头插会造成死循环情况说明_java hashmap扩容在jdk1.8中由头插改为尾插(为了避免死循环问题)-优快云博客

1.8使用尾插法则解决这一问题

多线程中put可能导致元素丢失,多线程同时执行put时如果计算的索引位置相同那么则会造成前一个key值覆盖后一个key 在jdk18和17都是如此

put和get并发可能导致get为null,因为元素个数超过thredshold导致其他线程进行get时会读出空18和17中都是如此

ConcurrentHashMap实现原理

1、它本质上还是一个hashmap所以存储hash冲突

2、他与hashmap的区别是他可以支持多线程因为它通过在Node节点去加锁来保证数据更新的安全性

3、性能方面的优化(如何在高并发数据安全性之间做好平衡)

 cpu的三级缓存、mysql的 buffer_pool、Synchronized的锁升级类似的优化

1、1.8中他的锁数组中的某一个节点的粒度,在1.7中则是segement锁的范围更大所以性能更低

2、引入红黑树降低了查的一个时间复杂度

3、扩容机制,既然我们已经是一个多线程的集合那扩容机制肯定有了质的升级,它引入了多线程并发扩容的一个实现,简单来说就是多个线程对原始的数组进行分片,分片之后每个线程去负责一个分片的数据迁移,从而整体提升扩容过程中数据迁移的效率

4、它有一个size方法获取总的元素个数,而在多线程并发场景中在保证原子性的前提下去实现元素个数的累加性能非常低,所以它做了2个优化,当线程竞争不激烈时采用CAS的方式来实现元素个数的递增如果竞争激烈使用数组来维护元素个数,如果要增加总的元素个数直接从数组中随机选择一个再通过CAS算法原子递增核心思想是引入数组来并发更新的一个负载元素添加过程中如果发现正在扩容其他添加元素的线程也会扩容然后再添加元素到扩容后的数组

1. 什么是 CAS

CAS(Compare And Swap,比较并交换),通常指的是这样一种原子操作:针对一个变量,首 先比较它的内存值与某个期望值是否相同,如果相同,就给它赋一个新值。 CAS 的逻辑用伪代码描述如下:

CAS可以看做是乐观锁(对比数据库的悲观、乐观锁)的一种实现方式,Java原子类中的递增操 作就通过CAS自旋实现的。

CAS是一种无锁算法,在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。

+

spring

spring bean配置原理(扫描包)

spring中的类使用@controller、@Service、@Repostory 

启动类会扫描这些注解下的然后注册到spring的ioc容器中

这些被注册进去的类就成为bean 与new 不同

他们是通过getBean方法创建

当需要使用时使用@AutoWrite注入到需要的类中进行使用

ioc是为了解决耦合问题、因为每次要new的话产生了耦合所有把所有需要使用的类

都直接放在ioc容器中、降低耦合性

spring ioc和DI区别

IOC为控制反转 DI为依赖注入

ioc是一种设计模式 是将本应由程序元来控制的类生命周期交给spring来创建并管理其的生命周期 从而吧创建类的活交给spring来控制反转

DI为依赖注入是ioc的一种实现方式通过构造函数、get\set方法返回给对象在Spring中可以使用xml和注解来实现就比如@Date @AllContr....

spring ioc的实现机制

spring ioc 通过简单工厂设计模式+Bean反射 传入一个标识生产相应对象

Factory需要传入标识就会自动生产对象

但标识这个东西写起来还是麻烦咯

所以我们直接用工厂创建bean! factory.getbean(位置)

而我们看了getBean的返回值就知道他会返回一个唯一标识这恰好可以当作Factory的标识来通过工场创建

至此一气呵成扔给了ioc容器完成控制反转

单例bean优势

1、如果每次都创建一个实力会增加内存消耗、ioc有反射会增加栈的深度

所以单例bean增加了服务器内存利用率

2、减少了jvm垃圾回收的负担

3、快速的获取到对象

springBean线程安全吗

spring AOP面向切面编程

为解耦而生、公共的东西交给AOP去处理(日志处理、事物处理、权限控制这些东西通过AOP去解决)

设计模式

工厂模式(解耦)

简单工厂模式(没有实现对修改关闭,对扩展开放)开闭原则

工厂方法模式(实现了开闭原则)

只能解决创建同类对象

抽象工厂设计模式(升级版可以生产多个等级的产品)

策略模式

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值