Java面试题---多线程

本文详细介绍了Java多线程的相关面试知识点,包括并行与并发的区别、线程与进程的区别、守护线程概念、线程创建方式、线程状态、线程同步方法如sleep()和wait()的区别、线程池的使用以及防止死锁的策略等。还讨论了ThreadLocal、synchronized的底层实现以及与Lock的区别。此外,文章还提到了线程安全的保障措施,如使用并发包、synchronized和Lock。
摘要由CSDN通过智能技术生成

多线程

整理了一些关于多线程方面的面试题,答案多是来自网络。
1.并行和并发有什么区别?
并发的关键是你有处理多个任务的能力,但不是同时。
并行的关键是你在同时处理多个任务。
例子:
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发(不一定是同时的)。
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。

2.线程和进程的区别?
进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。

所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)

内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。

包含关系:只有一个线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

3.守护线程是什么?
守护线程,专门用于服务其他的线程,如果其他的线程(即用户自定义线程)都执行完毕,连main线程也执行完毕,那么jvm就会退出(即停止运行)——此时,连jvm都停止运行了,守护线程当然也就停止执行了。

4.创建线程有哪几种方式?
继承Thread类创建线程类
通过Runnable接口创建线程类
通过Callable和Future创建线程
通过Executor 的工具类可以创建三种类型的普通线程池:
FixThreadPool(int n); 固定大小的线程池
SingleThreadPoolExecutor :单线程池
CashedThreadPool(); 缓存线程池

5.说一下 runnable 和 callable 有什么区别?
(1)Callable规定的方法bai是call(),Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
(3)call方法可以抛出异常,run方法不可以

6.线程有哪些状态?
新建状态(New)
就绪状态(Runnable)
运行状态(Running)
阻塞状态(Blocked)
死亡状态(Dead)

7.sleep() 和 wait() 有什么区别?
这两个方法来自不同的类分别是Thread和Object,sleep方法属于Thread类中的静态方法,wait属于Object的成员方法。
sleep()是线程类(Thread)的方法,不涉及线程通信,调用时会暂停此线程指定的时间,但监控依然保持,不会释放对象锁,到时间自动恢复;wait()是Object的方法,用于线程间的通信,调用时会放弃对象锁,进入等待队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才进入对象锁定池准备获得对象锁进入运行状态。
wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)。
sleep()方法必须捕获异常InterruptedException,而wait()\notify()以及notifyAll()不需要捕获异常。

8.notify()和 notifyAll()有什么区别?
如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争。
优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

9.线程的 run()和 start()有什么区别?
调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();直接调用 run() 方法,无法达到启动多线程的目的,相当于主线程线性执行 Thread 对象的 run() 方法。
一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常;run() 方法没有限制。

10.创建线程池有哪几种方式?
通过Executors工厂方法创建
通过new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)自定义创建

11.线程池都有哪些状态?
(1.RUNNING:这是最正常的状态,接受新的任务,处理等待队列中的任务。线程池的初始化状态是RUNNING。线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0。
(2.SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。调用线程池的shutdown()方法时,线程池由RUNNING -> SHUTDOWN。
(3.STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。调用线程池的shutdownNow()方法时,线程池由(RUNNING or SHUTDOWN ) -> STOP。
(4.TIDYING:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()。因为terminated()在ThreadPoolExecutor类中是空的,所以用户想在线程池变为TIDYING时进行相应的处理;可以通过重载terminated()函数来实现。
当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
(5.TERMINATED:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

12.线程池中 submit()和 execute()方法有什么区别?
接收的参数不一样。exucute只能执行实现Runnable接口的线程,submit可以执行实现Runnable接口或Callable接口的线程
submit有返回值,而execute没有

13.在 java 程序中怎么保证多线程的运行安全?
方法一:使用安全类,比如 Java. util. concurrent 下的类。
方法二:使用自动锁 synchronized。
方法三:使用手动锁 Lock。

14.多线程锁的升级原理是什么?
锁级别:无锁->偏向锁->轻量级锁->重量级锁
JVM优化synchronized的运行机制,当JVM检测到不同的竞争状态时,就会根据需要自动切换到合适的锁,这种切换就是锁的升级。升级是不可逆的,也就是说只能从低到高,也就是偏向–>轻量级–>重量级,不能够降级

15.什么是死锁?
是指多个线程在运行过程中因争夺资源而造成的一种僵局,当线程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进

16.怎么防止死锁?
资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件)
可剥夺资源:即当某线程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
资源有序分配法:系统给每类资源赋予一个编号,每一个线程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)

17.ThreadLocal 是什么?有哪些使用场景?
ThreadLocal 是线程本地存储,在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。
经典的使用场景是为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的 Connection; 还有 Session 管理 等问题。

18.说一下 synchronized 底层实现原理?
当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。

19.synchronized 和 volatile 的区别是什么?
1)volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.
2)volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.
3)volatile仅能实现变量的修改可见性,而synchronized则可以保证变量的修改可见性和原子性. 
4)volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.
5)当一个域的值依赖于它之前的值时,volatile就无法工作了,如n=n+1,n++等。如果某个域的值受到其他域的值的限制,那么volatile也无法工作,如Range类的lower和upper边界,必须遵循lower<=upper的限制。
6)使用volatile而不是synchronized的唯一安全的情况是类中只有一个可变的域

20.synchronized 和 Lock 有什么区别?
Synchronized是关键字,内置语言实现,Lock是接口。
Synchronized在线程发生异常时会自动释放锁,因此不会发生异常死锁。Lock异常时不会自动释放锁,所以需要在finally中实现释放锁。
Lock是可以中断锁,Synchronized是非中断锁,必须等待线程执行完成释放锁。
Lock可以使用读锁提高多线程读效率。

21.synchronized 和 ReentrantLock 区别是什么?
synchronized 竞争锁时会一直等待;ReentrantLock 可以尝试获取锁,并得到获取结果
synchronized 获取锁无法设置超时;ReentrantLock 可以设置获取锁的超时时间
synchronized 无法实现公平锁;ReentrantLock 可以满足公平锁,即先等待先获取到锁
synchronized 控制等待和唤醒需要结合加锁对象的 wait() 和 notify()、notifyAll();ReentrantLock 控制等待和唤醒需要结合 Condition 的 await() 和 signal()、signalAll() 方法
synchronized 是 JVM 层面实现的;ReentrantLock 是 JDK 代码层面实现
synchronized 在加锁代码块执行完或者出现异常,自动释放锁;ReentrantLock 不会自动释放锁,需要在 finally{} 代码块显示释放

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值