多线程和锁面试题

本文详细介绍了Java并发编程中的关键概念,包括并发编程三要素、线程与进程的定义、线程的创建方式、线程状态、线程池的创建、wait()、sleep()、yield()、join()方法的区别和用法,以及synchronized与Lock的区别。同时探讨了volatile关键字的作用、乐观锁和悲观锁的概念以及死锁的产生条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1、并发编程三要素

2、什么是线程和进程

3、创建线程的方式

4、Java线程具有五种基本状态

5、四种线程池的创建 (Excutors)

6、wait() 和 sleep() 方法的区别

7、线程的 sleep() 方法和 yield() 方法有什么区别?

8、线程的 join() 方法是干啥用的?

9、Thread 调用 start() 方法和调用 run() 方法的区别

10、synchronized 和 Lock 的区别

11、synchronized 各种加锁场景的作用范围

12、为什么要使用线程池?直接new个线程不是很舒服?

13、为什么 wait(), notify()和 notifyAll()必须在同步方法或者同步块中被调用?

14、volatile 关键字的作用


1、并发编程三要素

(1)原子性

(2)可见性

(3)有序性

2、什么是线程和进程

        进程是指在系统中正在运行的一个应用程序,程序一旦运行就是进程。比如.exe文件运行,进程就可以视为程序的一个实例,大部分程序都可以运行多个实例进程

        线程是进程的一个子集,一个线程就是一个指令流的执行,线程按照一定的顺序把这些指令流交给CPU执行,就是线程的执行

3、创建线程的方式

(1)继承Thread类

(2)实现Runnable接口

(3)通过Callable 返回值 Future

(4)通过线程池

4、Java线程具有五种基本状态

(1)新建状态(New)

(2)就绪状态(Runnable)

(3)运行状态(Running)

(4)阻塞状态(Blocked)

(5)死亡状态(Dead)

5、四种线程池的创建 (Excutors)

(1)newCachedThreadPool创建一个可缓存线程池

(2)newFixedThreadPool创建一个定长线程池,可控制线程最大并发数

(3)newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行

(4)newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务

6、wait() 和 sleep() 方法的区别

1. sleep() 来自 Thread 类,wait() 来自 Object 类。

2. sleep 不会让线程释放同步锁的。wait() 会释放同步锁,让其他线程进入 synchronized 代码块执行。

3. sleep() 可以在任何地方使用。wait() 只能在同步控制方法或者同步控制块里面使用,否则会抛 IllegalMonitorStateException。

4. sleep() 在时间到了之后会重新恢复;wait() 则需要其他线程调用同一对象的 notify()/nofityAll() 才能重新恢复。

7、线程的 sleep() 方法和 yield() 方法有什么区别?

线程执行 sleep() 方法后进入超时等待(TIMED_WAITING)状态,而执行 yield() 方法后进入就绪(READY)状态。

sleep() 方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程运行的机会;yield() 方法只会给相同优先级或更高优先级的线程以运行的机会。

8、线程的 join() 方法是干啥用的?

用于等待当前线程终止。如果一个线程A执行了 threadB.join() 语句,其含义是:当前线程A等待 threadB 线程终止之后才从 threadB.join() 返回继续往下执行自己的代码。

9、Thread 调用 start() 方法和调用 run() 方法的区别

run():普通的方法调用,在主线程中执行,不会新建一个线程来执行。

start():新启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到 CPU 时间片,就开始执行 run() 方法。

10、synchronized 和 Lock 的区别

1)Lock 是一个接口;synchronized 是 Java 中的关键字,synchronized 是内置的语言实现;

2)Lock 在发生异常时,如果没有主动通过 unLock() 去释放锁,很可能会造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁;synchronized 不需要手动获取锁和释放锁,在发生异常时,会自动释放锁,因此不会导致死锁现象发生;

3)Lock 的使用更加灵活,可以有响应中断、有超时时间等;而 synchronized 却不行,使用 synchronized 时,等待的线程会一直等待下去,直到获取到锁;

11、synchronized 各种加锁场景的作用范围

1. 作用于普通方法,锁住的是对象实例(this),每一个对象实例有一个

2. 作用于静态方法,锁住的是类的Class对象

3. 作用于 this,锁住的是对象实例,每一个对象实例有一个锁。

4. 作用于静态成员变量,锁住的是该静态成员变量对象

12、为什么要使用线程池?直接new个线程不是很舒服?

如果我们在方法中直接new一个线程来处理,当这个方法被调用频繁时就会创建很多线程,不仅会消耗系统资源,还会降低系统的稳定性,一不小心把系统搞崩了,就可以直接去财务那结帐了。

1. 降低资源消耗。通过重复利用已创建的线程,降低线程创建和销毁造成的消耗。
2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
3. 增加线程的可管理型。线程是稀缺资源,使用线程池可以进行统一分配,调优和控。

13、为什么 wait(), notify()和 notifyAll()必须在同步方法或者同步块中被调用?

wait() 方法强制当前线程释放对象锁。这意味着在调用某对象的 wait() 方法之前,当前线程必须已经获得该对象的锁。 因此,线程必须在某个对象的同步方法或同步代码块中才能调用该对象的wait() 方法。

在调用对象的 notify() 和 notifyAll() 方法之前,调用线程必须已经得到该对象的锁。 因此,必须在某个对象的同步方法或同步代码块中才能调用该对象的 notify() 或 notifyAll() 方法。

14、volatile 关键字的作用

1、可见性,当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。

2、禁止指令重排,保证变量前后代码的执行顺序;

15、乐观锁和悲观锁

悲观锁
悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观地认为,不加锁的并发操作一定会出问题。

乐观锁
乐观锁正好和悲观锁相反,它获取数据的时候,并不担心数据被修改,每次获取数据的时候也不会加锁,只是在更新数据的时候,通过判断现有的数据是否和原数据一致来判断数据是否被其他线程操作,如果没被其他线程修改则进行数据更新,如果被其他线程修改则不进行数据更新

16、死锁

产生死锁的必要条件

1. 互斥条件:所谓互斥就是线程在某一个时间内独占资源

2. 请求与保持条件:⼀个线程因请求资源⽽阻塞时,对已获得的资源保持不放。

3. 不剥夺条件:线程已获得资源,在未使用完之前,不能强行剥夺。

4. 循环等待条件:若⼲线程之间形成⼀种头尾相接的循环等待资源关系

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值