Java常见面试题之多线程

目录

多线程考核点【重点】1.什么是进程?什么是线程?‘

2.多线程优缺点?

3.什么是并发?什么是并行?

4.Java 中实现线程方式?

5.runnable 和 callable 区别?

6.守护线程和普通线程区别?

7.线程常用方法

8.run 和 start 区别?

9.线程的5个状态

10.线程的 sleep()方法和 yield0 方法有什么不同?

11.什么是死锁、如何避免

12.sleep和wait 区别?线程协作(线程经典案例--Object(wait(n),notify0,notifiyAll())

13.什么是ThreadLocal? 【重点】

14.ThreadLocal是否会内存泄漏?

​编辑

15.解释一下强引用、软引用、弱引用、虚引用?

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

17.execute()和submit() 区别?

18.shutdown()和shutdownNow()区别?

19.volatile 的作用和特性?

20.volatile和synchronized 区别?

21.什么是乐观锁?什么是悲观锁?

22.什么是 GC、作用是什么

23.GC回收的区域在哪里?

25.JVM内存结构

26.GC常用算法


多线程考核点【重点】
1.什么是进程?什么是线程?‘

线程是处理器任务调度和执行的基本单位,进程是操作系统资源分配的基本单位。
进程是程序的一次执行过程,是系统运行的基本单位。线程是一个比进程更小的执行单位,一个进程可以包含多个线程。

2.多线程优缺点?

、优点:当一个线程进入等待状态或者阻塞时,CPU 可以先去执行其他线程,提高 CPU 的利用。
b、缺点:
1. 上下文切换:频繁的上下文切换会影响多线程的执行速度。
2. 不恰当的编程有可能会出现死锁
3. 资源限制:在进行并发编程时,程序的执行速度受限于计算机的硬件或软件资源。在并发编程中,程序执行变快的原因是将程序中串行执行的部分变成并发执行,如果因为资源限制,并发执行的部分仍在串行执行,程序执行将会变得更慢,因为程序并发需要上下文切换和资源调度。

3.什么是并发?什么是并行?

并发是指一个处理器同时处理多个任务。 
并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。 
两者区别:并发是交替执行,并行是同时执行。

4.Java 中实现线程方式?

1. 继承 Thread 类
lass MyThread01 extends Thread{
    @Override
    public void run() {
        System.out.println(2);
    }
}
2. 实现 Runnable 接口
class MyRun implements Runnable{
    public void run() {
        System.out.println(2);
    }
}
3. 实现 Callable 接口
class MyCallable implements Callable<Integer> {
    public Integer call() throws Exception {
        return 100;
    }
}

 实现类

public class Test03 {
     public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Integer> futureTask = new  FutureTask<>(new MyCallable());
        //创建线程并启动
        new Thread(futureTask).start();
        //等我执行完毕返回结果
        int result = futureTask.get();
        System.out.println(result);
     }
}

5.runnable 和 callable 区别?

相同点:
两者都是接口
两者都需要调用 start() 启动线程
不同点:
callable 的核心是 ca11() 方法,允许返回值,runnable 的核心是 run() 方法,没有返回值
ca11() 方法可以抛出异常,但是 run() 方法不行

6.守护线程和普通线程区别?

守护线程又称为“服务线程”。在没有用户线程可服务时会自动离开

7.线程常用方法

currentThread():返回正在执行的线程对象
sleep():使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。
join():等待该线程执行结束
yield():放弃被CPU调度权,让CPU重新调用线程(自己和其他线程)
start():启动线程
stop():结束线程
setPrority(n):设置线程优先级

8.run 和 start 区别?

线程是通过 Thread 对象所对应的方法 run() 来完成其操作的,而线程的启动是通过 start() 方法执行的。
run() 方法可以重复调用,start() 方法只能调用一次

9.线程的5个状态

1、新建状态(New)
2、就绪状态(Runnable)
3、运行状态(Running)
4、阻塞状态(Blocked)
5、死亡状态(Dead)

10.线程的 sleep()方法和 yield0 方法有什么不同?

1.  s1eep方法会使得当前线程暂停指定的时间,没有消耗 CPU 时间片。
2.  sleep使得线程进入到阻塞状态,yield() 只是对 CPU 进行提示,如果 CPU 没有忽略这个提
示,会使得线程上下文的切换,进入到就绪状态。
3. sleep() 一定会完成给定的休眠时间,yield() 不一定能完成。
4. sleep() 需要抛出 InterruptedException,而 yield() 方法无需抛出异常。

11.什么是死锁、如何避免

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结束。

产生死锁的四个必要条件
互斥条件,进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

不可剥夺条件,进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。

请求与保持条件,进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

循环等待条件,存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, …, pn},其中Pi等 待的资源被P(i+1)占有(i=0, 1, …, n-1),Pn等待的资源被P0占有

避免死锁:1、不要嵌套锁;2、合理的顺序 A-B-C 3.降低锁的使用粒度,尽量不要几个功能用同一把锁,能锁块不锁方法

12.sleep和wait 区别?线程协作(线程经典案例--Object(wait(n),notify0,notifiyAll())

1. sleep 是线程中的方法,但是 wait 是 Object 中的方法。
2. sleep 方法不会释放 lock,但是 wait 会释放,而且会加入到等待队列中。
3. sleep 方法不依赖于同步器 synchronized,但是 wait 需要依赖 synchronized 关键字。
4. sleep 不需要被唤醒(休眠之后退出阻塞),但是 wait 需要(不指定时间需要被别人中断)

13.什么是ThreadLocal? 【重点】

Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。所以ThreadLocal的应用场合,最适合的是按线程多实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。

数据隔离的秘诀其实是这样的,Thread有个TheadLocalMap类型的属性,叫做threadLocals,该属性用来保存该线程本地变量。这样每个线程都有自己的数据,就做到了不同线程间数据的隔离,保证了数据安全。

ThreadLocal 叫做本地线程变量,意思是说,ThreadLocal 中填充的的是当前线程的变量,该变量对其他线程而言是封闭且隔离的,ThreadLocal 为变量在每个线程中创建了一个副本,这样每个线程都可以访问自己内部的副本变量

14.ThreadLocal是否会内存泄漏?

 

会。
当仅仅只有 ThreadLocalMap 中的 Entry 的 key 指向 ThreadLocal 的时候,ThreadLocal 会进行回收的!!!
ThreadLocal 被垃圾回收后,在 ThreadLocalMap 里对应的 Entry 的键值会变成 null,但是 Entry 是强引用,那么 Entry 里面存储的 Object,并没有办法进行回收,所以有内存泄漏的风险。


15.解释一下强引用、软引用、弱引用、虚引用?

强引用(StrongReference):一般指的是对象被new出来,强引用一般不会被jvm收回
如:Object obj = new Object();
软引用(SoftReference):软引用相对来说弱于强引用,当内存足够的时候不会被GC回收,但内存不足时,再试图回收软引用,通过软引用可以做数据缓存。
SoftReference<String> softRef = new SoftReference<String>();
弱引用(WeakReference):区别于软件引用是生命周期更短,当GC回收启动发现弱引用不管内存满不满,都会被直接回收。
Object obj = new Object();
WeakReference<Object> weekRef =new WeakReference<Object>(obj);
obj = null;
System.gc();
wf.get();//null;
虚引用(PhantomReference):这个引用也有人叫幻引用,也很明显,引用一个不存在,随时会被干掉,算是所有引用中最容易被干掉的。
Object obj = new Object();
ReferenceQueue rq = new ReferenceQueue();
PhantomReference<Object> pr =new PhantomReference<Object>(rq,obj);
pr.get();//null;

```

总结:4种引用强度从高到低:强引用 -> 软引用 -> 弱引用 -> 虚引用 

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

降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度,当任务到达时,任务可以不需要等到线程创建就立即执行。
提高线程的可管理性,线程是稀缺资源,无限制创建,不仅会消耗系统资源,还会降低系统稳定性,使用线程池可以统一分配。

17.execute()和submit() 区别?

execute()方法只能执行 Runnable 类型的任务。submit () 方法可以执行 Runnable 和 ca11ab1e 类型的任务。
submit()方法可以返回持有计算结果的 Future 对象,同时还可以抛出异常,而 execute() 方法不可以。

18.shutdown()和shutdownNow()区别?

shutdown() 会将线程池状态置为 SHUTDOWN,不再接受新的任务,同时会等待线程池中已有的任务执行完成再结束。
shutdownNow()会将线程池状态置为SHUTDOWN,对所有线程执行interrupt()操作,清空队列,并将队列中的任务返回回来。

19.volatile 的作用和特性?

作用:volatile 是一个轻量级的 synchronized,一般作用于变量,在多处理器开发的过程中保证了内存的可见性。相比于 synchronized 关键字,volatile 关键字的执行成本更低,效率更高。
特性:
可见性:volatile 可以保证不同线程对共享变量进行操作时的可见性。即当一个线程修改了共享变量时,另一个线程可以读取到共享变量被修改后的值。
有序性:volatile 会通过禁止指令重排序进而保证有序性。 
原子性:synchronized

20.volatile和synchronized 区别?

要想保证数据安全、必须具备三个条件:原子性、有序性、可见性
volatile :解决可见性和有序性
synchronized:解决原子性的问题
volatile 是在告诉 jvm 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取,synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
volatile 仅能使用在变量级别;synchronized 则可以使用在变量、方法、和类级别的
volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性
volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化

21.什么是乐观锁?什么是悲观锁?

悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。
乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试)。
悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。
乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。

22.什么是 GC、作用是什么

GC:垃圾回收机制、清理对象,整理内存的工作

23.GC回收的区域在哪里?

JVM 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭, 栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理。因此, 我们的内存垃圾回收主要集中于 Java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的。

25.JVM内存结构

 

 

 

1,堆的GC操作采用分代收集算法。
2,堆区分了新生代和老年代;
3,新生代又分为:Eden空间、From Survivor(S0)空间、To Survivor(S1)空间。分配比例:8:1:1
```

```
1 在初始阶段,新创建的对象被分配到Eden区,survivor的两块空间都为空。
2 当Eden区满了的时候,minor garbage 被触发 。
3 经过扫描与标记,存活的对象被复制到S0,不存活的对象被回收, 并且存活的对象年龄都增大一岁
4 在下一次的Minor GC中,Eden区的情况和上面一致,没有引用的对象被回收,存活的对象被复制到survivor区。当Eden 和 s0区空间满了,S0的所有的数据都被复制到S1
5.经过几次Minor GC之后,当存活对象的年龄达到一个阈值之后(-XX:MaxTenuringThreshold默认是15),就会被从年轻代Promotion到老年代。
6.随着MinorGC一次又一次的进行,不断会有新的对象被promote到老年代。
7.虚拟机会检查每次晋升进入老年代的大小是否大于老年代的剩余空间大小,如果大于,则直接触发一次Full GC

26.GC常用算法

GC 常用算法有:标记-清除算法,标记-压缩算法,复制算法,分代收集算法。目前主流的 JVM(HotSpot)采用的是分代收集算法。

a、标记-清除算法

为每个对象存储一个标记位,记录对象的状态(活着或是死亡)。分为两个阶段,一个是标记阶段,这个阶段内,为每个对象更新标记位,检查对象是否死亡;第二个阶段是清除阶段,该阶段对死亡的对象进行清除,执行 GC 操作。

b、标记-压缩算法(标记-整理)

标记-压缩法是标记-清除法的一个改进版。同样,在标记阶段,该算法也将所有对象标记为存活和死亡两种状态;不同的是,在第二个阶段,该算法并没有直接对死亡的对象进行清理,而是将所有存活的对象整理一下,放到另一处空间, 然后把剩下的所有对象全部清除。这样就达到了标记-整理的目的。

c、复制算法

该算法将内存平均分成两部分,然后每次只使用其中的一部分,当这部分内存满的时候,将内存中所有存活的对象复制到另一个内存中,然后将之前的内存清空,只使用这部分内存,循环下去。

d、分代收集算法

现在的虚拟机垃圾收集大多采用这种方式,它根据对象的生存周期,将堆分为新生代(Young)和老年代(Tenure)。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用复制算法。老年代里的对象存活率较高, 没有额外的空间进行分配担保,所以可以使用标记-整理 或者 标记-清除。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值