多线程部分面试题整理

并行和并发

并发:指两个或多个事件在同一个时间段内发生。(单核)
并行:指两个或多个事件在同一时刻发生(同时发生,多核)

自定义线程的方式

创建线程方式有四种:
继承Thread类,重写run方法
实现Runnable接口,实现run方法
实现Callable接口,通过包装器FutrueTask创建线程(有返回值)
线程池创建

线程的生命周期

新建(NEW),就绪(Runnable),运行(Running),阻塞(Blocked)(又分为 Blocked,waiting,time-waiting),死亡(Dead/TERMINATED)

线程的常见方法

**start()😗*启动一个新线程,是线程进入就绪态等待cpu调度。
**sleep()😗*线程休眠, 每一个对象都有一个锁,sleep不会释放锁,使线程停止运行一段时间,将处于阻塞状态,如果调用了sleep方法之后,没有其他等待执行的线程,这个时候当前线程不会马上恢复执行!线程由运行状态进入阻塞状态,时间一到,再回到预备状态,等待CPU的重新调度。

join(): join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞

yield(): 线程直接由运行状态跳回预备状态。让当前正在执行线程暂停,不是阻塞线程,而是将线程转入就绪状态。调用了yield方法之后,如果没有其他等待执行的线程,此时当前线程就会马上恢复执行!

setDaemon(): 在start方法之前调用;默认是false;设为true时设置线程为守护线程

setPriority(): 线程的优先级代表的是概率,范围从1到10,默认为5。Java提供:一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。

如何保证线程的执行顺序

方案一:使用join()方法
方案二:单线程线程池

守护线程和用户线程

在 Java 中通常有两种线程:守护线程(Daemon Thread)和用户线程(User Thread)。

守护线程:如果只剩下守护线程未离开,JVM是可以离开的,守护线程是JVM中所有用户线程的保姆。,在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT 线程都是守护线程

用户线程:当存在任何一个用户线程未离开,JVM是不会离开的。可以理解为是系统的工作线程,它会完成这个程序需要完成的业务操作。如我们使用 Thread 创建的线程在默认情况下都属于用户线程

sleep和wait的区别

1,这两个方法来自不同的类:wait是Object下的方法和sleep是Thread下的方法。
2,最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
3,wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在
任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
4,sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常

run和start的区别

start()
用 start方法来启动线程,是真正实现了多线程, 通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法。但要注意的是,此时无需等待run()方法执行完毕,即可继续执行下面的代码。所以run()方法并没有实现多线程。

run()
run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码。

区别
1、线程中的start()方法和run()方法的主要区别在于,当程序调用start()方法,将会创建一个新线程去执行run()方法中的代码。但是如果直接调用run()方法的话,会直接在当前线程中执行run()中的代码,注意,这里不会创建新线程。这样run()就像一个普通方法一样。

2、另外当一个线程启动之后,不能重复调用start(),否则会报IllegalStateException异常。但是可以重复调用run()方法。

总结起来就是run()就是一个普通的方法,而start()会创建一个新线程去执行run()的代码。

如何保证线程安全

1.lock锁
2.synchronize锁
3.CAS(自旋锁)
4.分布式锁

常见的线程池有哪些

1.单线程线程池
2.固定大小的线程池(核心线程数=最大线程数)
3.可缓存的线程池
4.可定时和周期执行任务的线程池
newSingleThreadExecutor:单线程线程池。只有一个线程,可以确保线程执行顺序。 使用了 LinkedBlockingQuene无界队列 撑爆内存
newFixedThreadPool:固定长度大小的线程池。 一样
newCachedThreadPool:可缓存的线程池。线程数量不做限制。线程空闲时间超过一定一般为60s会释放资源 有新任务没空闲的话会创线程可能导致性能下降
newScheduledThreadPool:一个定长的线程池,支持定时及周期性任务执行。

不建议使用

newFixedThreadPool:无界队列过多的任务导致程序崩溃,线程池无法根据并发动态扩容。

newSingleThreadExecutor:无界队列过多的任务导致程序崩溃,线程池无法根据并发动态扩容

newCachedThreadPool:同步队列,过大的并发将会创建很多线程,且线程无上限,可能导致程序崩溃。
newScheduledThreadPool:无界队列过多的任务导致程序崩溃,线程池无法根据并发动态扩容,且有可能导致任务延时。

如何自定义线程池 —

使用ThreadPoolExecutor创建线程池。
new ThreadPoolExecutor();
填入七大参数

自定义线程池的七大参数 —

1、corePoolSize:核心线程数,线程池中最少线程,核心线程不会被回收。

2、maximumPoolSize:最大线程数,线程池中最多线程,包含核心线程数,不能小于核心线程数。

3、keepAliveTime:非核心线程(除去核心线程之外的线程)存活时间,如果非核心线程的空闲时间大于此参数,将会被回收。

4、TimeUnit:时间单位,参数keepAliveTime的时间单位。

5、BlockingQueue:阻塞工作队列,当来一个新的线程任务时,如果当前没有空闲线程,此线程任务将会进入阻塞工作队列中进行等待。

6、ThreadFactory:线程工厂,用于创建线程,自定义线程的名称。(需要实现ThreadFactory接口,实现newThread()方法,在此方法中,新建Thread对象,通过调用setName()方法给线程定义名称。)

7、RejectedExecutionHandler:拒绝策略,当线程池中没有空闲线程,且阻塞工作队列已满,且最大线程数也已超出,此时再来线程任务将执行拒绝策略。

有四种拒绝策略:第一种:AbortPolicy,丢弃任务并且抛出异常(默认方式)。第二种:DiscardPolicy,丢弃任务不抛异常。第三种:DiscardOldestPolicy,将阻塞工作队列中的队头任务丢弃,将当前任务加入阻塞工作队列。第四种:CallerRunsPolicy,谁调用谁处理。

线程在线程池中的执行流程 —

在这里插入图片描述

常见拒绝策略

AbortPolicy(抛出异常):默认的拒绝策略。当任务无法被提交给线程池时,会直接抛出RejectedExecutionException异常。

CallerRunsPolicy(调用者运行):当任务无法被提交给线程池时,会由提交任务的线程自己执行该任务。

DiscardPolicy(直接丢弃):当任务无法被提交给线程池时,直接丢弃该任务,没有任何提示或处理。

DiscardOldestPolicy(丢弃最旧任务):当任务无法被提交给线程池时,会丢弃队列中最早的一个任务,然后尝试再次提交当前任务。

ThreadLocal – 频率不高

ThreadLocal叫做线程变量或者线程副本,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。
ThreadLocal 适用于如下两种场景:
1、每个线程需要有自己单独的实例
2、实例需要在多个方法中共享,但不希望被多线程共享
常见的如:存储用户Session,数据库连接
使用ThreadLocal会有内存泄漏的风险:如果没有将ThreadLocal内的变量删除(remove),它的生命周期将会与线程共存。在高并发环境或者线程池中,导致内存占用过大,从而出现OOM。
和Synchronized的区别: synchronized 用于线程间的数据共享,利用锁机制,使方法或代码块某一时刻只能被一个线程访问。ThreadLocal为每个线程提供了变量的副本,该变量对于其他线程是隔离的。
实现原理:ThreadLocal底层是靠ThreadLocalMap实现的,ThreadLocalMap是ThreadLocal的静态内部类,set数据的时候会先获取当前线程的ThreadLocalMap对象,将当前的ThreadLocal作为key,set的数据作为value。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值