线程常见面试题

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

进程:

程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至CPU,数据加载至内存,在指令运行过程中还需要用到磁盘,网络等设备,进程就是用来加载指令,管理内存,管理IO的.

当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程.

进程就可以视为程序的一个实例,大部分程序可以同时运行多个实例进程(例如记事本,画图,浏览器等),也有的程序只能启动一个实例进程(例如网易云音乐,360安全卫士等).

线程:

一个进程之内可以分为一到多个线程.

一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给CPU执行.

2.并发和并行有什么区别?

并发(concurrent)是同一时间应对多件事件的能力:

单核cpu下,线程实际还是串行执行的,操作系统中有一个组件叫做任务调度器,将cpu的时间片(windows下时间片最小约为5毫秒)分给不同的程序使用,只是由于cpu在线程间(时间片很短)的切换非常快,人的感觉是同时运行的,总结为一句话就是:微观串行,宏观并行,一般会将这种线程轮流使用CPU的做法称为并发,concrrent.

并行(parallel)是同一时间动手做多件事情的能力:

多核cpu下,每个核(core)都可以调度运行线程,这时候线程可以是并行的,

3.线程创建的4中方式?

方法一:直接使用Thread

// 创建线程对象
Thread t = new Thread() {
public void run() {
// 要执行的任务
}
};
// 启动线程
t.start();

方法二:使用Runnable配合Thread

Runnable runnable = new Runnable() {
public void run(){
// 要执行的任务
}
};
// 创建线程对象
Thread t = new Thread( runnable );
// 启动线程
t.start();

方法三:FutureTask配合Thread

// 创建任务对象
FutureTask<Integer> task3 = new FutureTask<>(() -> {
log.debug("hello");
return 100;
});
// 参数1 是任务对象; 参数2 是线程名字,推荐
new Thread(task3, "t3").start();
// 主线程阻塞,同步等待 task 执行完毕的结果
Integer result = task3.get();
log.debug("结果是:{}", result);

方法四:线程池

4.线程有哪些状态?

站在JavaAPI的角度:

1.新建(NEW): new Thread();      当使用new关键字创建Thread对象,此时的线程只是一个java对象并没有和操作系统真正的线程关联;

2.可运行状态(RUNABLE): 调用start方法,当调用线程对象的start,线程对象就会和操作系统真正的线程关联,可以由CPU调度执行;

3.终结状态:run方法中代码执行完毕,当线程中的代码被cup执行完毕后,线程就处于终结状态

新建->可运行->终结是不可逆的.

4.阻塞状态:没有获取线程锁的线程,线程在执行的时候,可能多个线程抢占同一把锁。没有获取锁的线程处于阻塞状态.

当获取锁的线程释放掉锁,重新抢占锁成功->可运行状态.

5.等待状态:获取锁的线程,调用锁对象.wait方法

当其他线程调用锁对象.notify方法唤醒等待状态的线程,重新获取锁成功-->可运行状态.

6.有时限的等待:

获取锁的线程,调用锁对象.wait(long)方法,当其他线程调用锁对象.notify方法唤醒等待状态的线程,

重新获取锁成功-->可运行状态.

时间到了重新获取锁成功--->可运行状态.

调用线程sleep(long)方法:

时间到了--->可运行状态,

5.为什么要使用线程池?

1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务.

2.可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程大概需要1MB内存,线程开的越多,消耗的内存就越大,最后死机).

6.线程池的构造方法里几个参数的作用分别都是什么?

线程池就是管理线程的一个容器;在java中线程池的实现,ThreadPoolExecutor.

ThreadPoolExecutor构造方法中的7个参数.

 

 

 corePoolSize核心线程数目- 池中会保留的最多线程数;

maximumPoolSize最大线程数目 - 核心线程+急救线程的最大数目;

keepAliveTime生存时间- 救急线程的生存时间,生存时间内没有新任务,此线程资源会释放,

unit 时间单位 - 救急线程的生存时间单位(秒, 毫秒等)

workQueue -工作队列,当没有空闲核心线程时,新来的任务会加入到此队列排队,队列满会创建救急线程执行任务,当我们使用submit方法向线程池中添加一个任务;如果所有的核心线程都在执行任务;该任务就会被加入WorkQueue

threadFactory线程工厂- 可以定制线程对象的创建,例如设置线程名字,是否是守护线程等

handler拒绝策略 - 当所有线程都在繁忙,workQueue也放满时,会触发拒绝策略.

 

7.线程池流程

1.当我们向线程池中添加任务(注意线程池中不能添加线程,只能添加任务,线程池中的线程是由线程池自己管理的)

2.有核心线程执行任务:

如果核心线程都在执行任务,将任务加入到任务队列 , 等待核心线程执行完成任务后,从队列中获取任务执行, 如果队列已经满了, 创建急救线程.由救急线程执行任务, 救急线程执行完成任务后, 如果在keepAliveTime时间内没有任务执行,则销毁.  如果救急线程也无法创建, 触发拒绝策略.

8.线程池的拒绝策略有哪些?

抛异常java.util.concurrent.ThreadPoolExecutor.AbortPolicy

由调用者执行任务 java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy

丢弃任务 java.util.concurrent.ThreadPoolExecutor.DiscardPolicy

丢弃最早排队任务,把新加任务添加到工作队列 java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy

9.notify()和notifyAll()有什么区别?

notify():唤醒锁对象等待区随机一个线程,

notifyAll()唤醒锁对象等待区的所有线程.

10.wait()和sleep()的区别?

相同点:wait() ,wait(long)和sleep(long)的效果都是让当前线程暂时放弃CPU的使用权,进去WATING Time_waiting的状态.

不同点: 

方法归属不同:sleep(long) 时thread的静态方法,而wait() ,wait(long) 都是Object的成员方法,每个对象都有,

醒来时机不同: 执行sleep(long)和wait(long)的线程都会在等待相应毫秒后醒来, wait(long) 和wait() 还可以被notify唤醒,wait()如果不唤醒就一直等待下去,(其他线程调用锁对象notify,可以把这个锁上处于wait状态的一个线程唤醒) 它们都可以被打断唤醒,

锁特性不同:

wait方法必须基于锁对象调用,直接调用报错,而sleep没有限制.

wait方法执行后会释放锁资源,允许其他线程获得该对象锁.

sleep如果在synchronized代码块中执行,并不会释放对象锁.

11.volatile能否保证线程安全

不能:

可见性:一个线程对共享变量进行了修改,另外一个线程能否看到修改后的结果

有序性:一个线程内代码是否按照编写顺序执行

原子性:一个线程内多行代码以一个整体运行,期间不能有其他线程的代码插队

volatile能否保证线程安全:

保证可见性,有序性(单例设计模式)

12.lock锁和synchronized锁区别

语法层面:

synchronized 是关键字:底层是通过c++代码实现锁(monitor)synchronized同步代码块执行完毕会自动释放锁

lock是一个接口:用 java 语言实现,需要手动调用 unlock 方法释放锁

功能层面:

具备基本的互斥、同步、锁重入功能 

Lock 提供了许多 synchronized 不具备的功能:获取阻塞状态的线程,公平锁,可超时tryLock,多条件变量,Lock 有适合不同场景的实现

13.悲观锁和乐观锁区别

悲观锁:悲观锁的核心代表是synchronized Lock,

思想:

1.只有获取锁的线程才能操作共享变量,每次只有一个线程能获取锁成功,获取锁失败的线程都要进入阻塞队列等待;

2.线程从可运行到阻塞,再从阻塞到可运行,涉及到上下文切换,如果频繁发生会影响效率

3.synchronized和Lock在获取锁的时候,如果锁已经被其他线程占用,还会重试几次

乐观锁:乐观锁的代表是AtomicInteger,使用cas保证原子性

思想:

1.每次只有一个线程能修改成功共享变量,其他修改失败的线程不需要阻塞,不断重试直到成功

2.由于所有的线程一直运行,不需要阻塞,不牵扯频繁上下文切换

3.需要多核cup的支持 cup>线程数量

14.什么是cas

compare and switch(比较并交换)

作用:可以保证对共享变量的操作是原子性

原理:操作共享变量之前,先查看该共享变量是否进行了修改,如果进行了修改,重试;如果没有进行修改;修改共享变量

15.HashTable的默认初始容量是多少,扩容因子是多少?每次扩容多少?

/**
* Constructs a new, empty hashtable with a default initial capacity (11)
* and load factor (0.75).
*/
public Hashtable() {
this(11, 0.75f);
}

int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
int newCapacity = (oldCapacity << 1) + 1;

16.HashTable在计算索引的时候,为什么不进行二次hash

int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;

hashtable默认容量是11,是一个质数(素数);扩容后也是质数,如果是质数取模的分散性本身就比较好;

计算出来的索引分散性比较好

17.ConcurrentHashMap的initcapacity和loadFactor与HashMap的含义相同吗?

HashMap中initcapacity是数组的初始容量,loadFactor是扩容因子当HashMap中元素的数量>initcapacity * loadFactor,数组会扩容一倍

1.8 ConcurrentHashMap的initcapacity是预计在ConcurrentHashMap中存储的元素个数,数组的长度由ConcurrentHashMap来计算,结果是2的N次幂

18.ThreadLocal

谈谈您对ThreadLocal的理解:

ThreadLocal可以实现对象的线程隔离,让每一个线程各用个的资源对象,避免争用实现的线程安全

这种方案可以采用方法内的局部变量实现;

局部变量也可以保证线程安全

局部变量是无法在多个方法直接共享的

ThreadLocal可以在一个线程内进行资源共享,在一个线程的多个方法中实现共享资源对象

ThreadLocal的原理:

每一个线程都有一个成员变量,类型是ThreadLocalMap,用来存储资源对象

当调用ThreadLocal的set方法的时候,就是将ThreadLocal作为Key,将资源对象作为value存储到当前线程的ThreadLocalMap属性中

当调用ThreadLocal的get方法的时候,就是将ThreadLocal作为Key,从当前线程的ThreadLocalMap属性中获取资源对象

当调用ThreadLocal的remove方法的时候,就是将ThreadLocal作为Key,从当前线程的ThreadLocalMap属性中删除资源对象

对资源进行线程隔离的,实际上是每个线程中的ThreadLocalMap成员变量

ThreadLocalMap扩容机制:

如果频繁在某个线程中使用不同ThreadLocal添加元素

当元素个数超过容器2/3进行扩容

ThreadLocalMap的默认容量16

ThreadLocalMap索引冲突:

 

开放地址法

 19.@Async注解失效的

1.注解@Async的方法不是public方法

2.注解@Async的返回值只能为void或Future

3.注解@Async方法使用static修饰也会失效

4.spring无法扫描到异步类,没加注解@Async或@EnableAsync注解

5.调用方与被调用方不能在同一个类

6.类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象

7.在Async方法上标注@Transactional是没用的.但在Async方法调用的方法上标注@Transcational是有效的

posted @   英俊潇洒的萨克君  阅读(86)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示