栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JAVA多线程常见面试题(自答版)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JAVA多线程常见面试题(自答版)

1.线程和进程有什么区别

进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程,进程在执行过程中拥有独立内存单元,而多个线程共享内存资源,线程是进程的一个实体,是cpu调度和分配的基本单位,是比程序更小的能独立运行的基本单位

2.如何创建线程

通过继承Tread类:继承Tread,重写该类run()方法,run()就是线程的执行体,然后创建Tread实例,调用start()方法来执行线程

通过实现Runnable接口:实现Runnable接口,重写接口的run()方法,同样为执行体,没有返回值,再创建Runnable实现类的实例,将该实例作为Thread的target来创建Thread实例,调用start()方法执行线程

通过实现Callable接口:实现Callable接口,重写call()方法作为执行体,有返回值,再创建Callable实现类的实例,用FutureTask来包装该对象,将FutureTask对象作为Thread的target来创建Thread实例,调用start()方法执行线程

3.线程有哪些状态

创建:生成线程对象,没有调用start()方法,处于创建状态

就绪:调用start()后,进入就绪状态,但线程调度程序没把该线程设为当前线程

运行:线程调度程序将该线程设为当前线程,处于运行状态,执行run()中的代码

阻塞:线程正在运行中被暂停,处于阻塞状态

死亡:run()方法结束或调用stop(),线程死亡,并且无法再使用start()令其就绪

4.sleep()和wait()有什么区别

sleep()是线程类的静态方法,让线程进入睡眠状态,让出执行状态给其他线程,休眠结束后会与其他线程一同竞争,睡眠期间不会释放机锁,其他线程不能访问

wait()是Object类的方法,会将线程放入等待池中,释放机锁,其他线程能够访问

5.为什么要使用线程池

利用线程池管理并复用线程,控制最大并发量,防止重复创建和销毁线程,降低资源消耗

实现线程队列缓存和拒绝策略

实现定时执行,周期执行

隔离线程环境

6.线程池有哪几种创建方式

newFixedThreadPool(int nThreads):创建一个固定长度的线程池,没提交一个任务就创建一个线程,当达到最大数量就不会变化,线程也会一直存在

newCachedThreadPool():创建一个可缓存的线程池,线程池可以回收空闲线程,需求增加时可以自动创建新线程,规模不受限制

newSingleThreadExecutor():单线程的Executor,如果线程异常结束会创建一个新的代替他,确保任务安顺序执行

newScheduledThreadPool(int corePoolSize):创建固定长度的线程池,并且以延迟或定时的方式执行任务

7.线程池的7大参数

corePoolSize核心线程数:最小线程数,即使处于空闲状态也不会销毁

maximumPoolSize最大线程数:当线程数到达最大线程数时,继续提交的任务会存入工作队列

keepAliveTime空闲线程存活时间:当空闲线程在指定时间后就会被销毁

unit空闲线程存活时间单位:空闲时间的单位

workQueue工作队列:新任务提交后会先放入工作队列,任务调度是取出

threadFactory线程工厂:创建新线程时使用的工厂

handler拒绝策略:线程池和任务队列都达到上限,再提交任务就会执行拒绝策略

8.线程池有哪些状态

RUNNING:能够接收新任务,并对已添加的任务进行处理

SHUTDOWN:不接收新任务,但可以对已添加的任务进行处理

STOP:不接受新任务,不处理已添加的任务

TIDYING:所有任务已终止,任务队列和线程池任务为空,会执行钩子函数terminated()

TERMINATED:线程池彻底终止

9.为什么要使用多线程

使用多线程最主要的原因就是提高系统资源的利用率,多个线程同时运行,利用CPU的多个核心,提高并发能力和CPU利用率

10.什么是线程安全

当一个对象被多个线程访问时,不用考虑运行时环境下的调度和交替执行,也不需要额外的同步,不做额外操作,依然可以获得正确结果,这种情况就是线程安全的

线程安全在三个方面体现:

1.原子性:同一时刻只能有一个线程对数据进行操作,而且是不可打断的,要么全运行要么完全不运行

2.可见性:一个线程对对象的修改是其他线程可以看到的

3.有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,一般来说是无序的

11.如何保证多线程下的线程安全

如果涉及到原子性可以使用Atomic原子类

可以使用线程安全类,如StringBuffer

集合类可以考虑JUC(java.util.concurrent)包下的并发集合类

可以用到synchronized关键字和lock包下的锁相关类,多机部署下可用Redis分布式锁

12.synchronized的底层原理是什么

synchronized同步代码块是通过monitor监视锁来实现同步的,通过monitorenter指令指向同步代码块开始位置,试图获取monitor的持有权,内部有个计数器,当计数器为0时可以成功获取,获取后计数器设为1,在执行monitorexit指令后计数器变为0,锁被释放,若线程获取锁失败就会一直等待知道另一线程释放锁,由于monitor存在于java对象头中,所以java任意对象可以作为锁。

synchronized修饰的方法欸有monitorenter和monitorexit指令,取而代之的是“ACC_SYNCHRONIZED”标识

13.synchronized和volatile有什么区别

volatile只能用在变量上,synchronized可用在任意对象

volatile只保证可见性,synchronized还保证原子性

volatile禁用了指令重排序

volatile不会造成阻塞

14.synchronized和ReentrantLock有什么区别

synchronized线程执行完毕后会自动释放锁,ReentrantLock需要手动释放锁

synchronized是非公平锁,ReentrantLock默认非公平锁,可以设置为公平锁

synchronized会无限期等待锁,而ReentrantLock可以放弃等待锁,可以设置超时等待锁,到指定时间未获取锁就会返回

15.什么是公平锁和非公平锁

公平锁是按照线程访问顺序去获取对象锁,不按顺序是非公平锁

16.什么是悲观锁和乐观锁

悲观锁,每次访问资源都会加锁,执行完任务释放锁,synchronized和ReentrantLock就是悲观锁

乐观锁,不会加锁,但是修改对象的时候会查看对象是否被修改,有冲突就会自旋尝试,最常见的实现方法就是CAS

17.什么是CAS

CAS,全称Compare And Swap,涉及三个操作数:需要读写的内存值V,进行比较的值A,需要修改的值B,只有当V和A相等时,才会把V更新为B,否则会重复尝试

18.CAS存在的问题

长时间自旋会给CPU带来很大开销

只能保证一个共享变量的原子操作

会发生ABA问题:如果内存值原来是A,后来变成了B,又被改成了A,CAS会认为没有变化,实际上是有变化的,解决思路是可以在变量前加入版本号,每次更新加1,就从A-B-A变成了1A-2B-3A

19.死锁是如何产生的?怎么避免?

死锁产生需要满足四个条件:

1.互斥:一个资源每次只能被一个进程使用

2.请求和保持:一个进程获得资源后还会继续对其他资源发送请求,同时不会释放占有的资源

3.不可剥夺:进程获得的资源在使用前是不可被剥夺的

4.循环等待:进程之间首尾相接,相互等待对方占有的资源

解决方案:

程序设计时一次性申请所有需要的资源,避免等待其他资源

按序申请资源

占有部分资源的线程进一步申请资源,若申请不到就主动释放

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/930085.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号