Java多线程基础Q&A
2021-08-28 10:20:33 63 举报
AI智能生成
Java多线程基础Q&A
作者其他创作
大纲/内容
Thread类基础
Q: Thread的deprecated过期方法是哪3个?作用是啥?
A: stop(), 终止线程的执行。
suspend(), 暂停线程执行。
resume(), 恢复线程执行。
A: stop(), 终止线程的执行。
suspend(), 暂停线程执行。
resume(), 恢复线程执行。
Q: 废弃stop的原因是啥?
A: 调用stop时,会直接终止线程并释放线程上已锁定的锁。
线程内部无法感知,并且不会做线程内的catch操作!就像一个不会负责的渣男。
A: 调用stop时,会直接终止线程并释放线程上已锁定的锁。
线程内部无法感知,并且不会做线程内的catch操作!就像一个不会负责的渣男。
Q: stop的替代方法是什么?
A: interrupt()。
调用thread.interrupt()终止时, 不会直接释放锁,会把线程的状态改变, 标记为中断。
然后线程的一些方法可以根据当前线程状态进行对应的操作,例如Tread.sleep()会抛出interrupException错误。
但是业务代码中如果没有对线程状态进行监控,那么这并不会影响业务代码的继续运行。
A: interrupt()。
调用thread.interrupt()终止时, 不会直接释放锁,会把线程的状态改变, 标记为中断。
然后线程的一些方法可以根据当前线程状态进行对应的操作,例如Tread.sleep()会抛出interrupException错误。
但是业务代码中如果没有对线程状态进行监控,那么这并不会影响业务代码的继续运行。
Q:为什么不建议使用thread.isInterrupted(), 而是推荐使用thread.interrcupted()来检测线程是否中断?
A:因为后者在返回当前线程状态时, 同时会把线程状态置为初始值
A:因为后者在返回当前线程状态时, 同时会把线程状态置为初始值
Q: suspend/resume的废弃原因是什么?
A: :调用suspend不会释放锁。
如果线程A暂停后,他的resume是由线程B来调用的,但是线程B又依赖A里的某个锁,那么就死锁了。
A: :调用suspend不会释放锁。
如果线程A暂停后,他的resume是由线程B来调用的,但是线程B又依赖A里的某个锁,那么就死锁了。
Q: Thread.sleep()和Object.wait()的区别
A:sleep不会释放对象锁, 而wait会释放对象锁。
A:sleep不会释放对象锁, 而wait会释放对象锁。
Q:Runnable接口和Callable的区别。
A: Callable可以和Futrue配合,并且启动线程时用的时call,能够拿到线程结束后的返回值,call方法还能抛出异常。
A: Callable可以和Futrue配合,并且启动线程时用的时call,能够拿到线程结束后的返回值,call方法还能抛出异常。
Q: 线程A如下:
public class A extends Thread {
@Override
public void run() {
System.out.println("this.isAlive()=" + this.isAlive());
}
}
//把线程A作为构造参数,传给线程B
A a = new A();
Thread b = new Thread(a);
b.start()
此时会打印什么?
A:此时会打印false! 因为把a作为构造参数传入b中, b执行start时, 实际上是在B线程中去调用了 A对象的run方法,而不是启用了A线程。
public class A extends Thread {
@Override
public void run() {
System.out.println("this.isAlive()=" + this.isAlive());
}
}
//把线程A作为构造参数,传给线程B
A a = new A();
Thread b = new Thread(a);
b.start()
此时会打印什么?
A:此时会打印false! 因为把a作为构造参数传入b中, b执行start时, 实际上是在B线程中去调用了 A对象的run方法,而不是启用了A线程。
Q: 线程的6种状态是:
A:
New: 新建了线程,但是还没调用start
RUNNABLE: 运行, 就绪状态包括在运行态中
BLOCKED: 阻塞,一般是因为想拿锁拿不到
WAITING: 等待,一般是wait或者join之后
TIMED_WAITING: 定时等待,即固定时间后可返回,一般是调用sleep或者wait(时间)的。
TERMINATED: 终止状态。
A:
New: 新建了线程,但是还没调用start
RUNNABLE: 运行, 就绪状态包括在运行态中
BLOCKED: 阻塞,一般是因为想拿锁拿不到
WAITING: 等待,一般是wait或者join之后
TIMED_WAITING: 定时等待,即固定时间后可返回,一般是调用sleep或者wait(时间)的。
TERMINATED: 终止状态。
synchronized关键字
即可修饰代码块, 也可修饰方法体:
修饰普通方法 -> 对象锁
修饰静态方法 -> 类锁
修饰普通方法 -> 对象锁
修饰静态方法 -> 类锁
Q: 调用下面的f()时,会出现死锁吗?
class A{
synchroized f(){
t()
}
synchroized t(){
}
}
A:不会。1个线程内, 可以重复进入1个对象的synchroized 块。
原理:当线程请求自己的锁时。JVM会记下锁的持有者,并且给这个锁计数为1。如果该线程再次请求自己的锁,则可以再次进入,计数为2.退出时计数-1. 直到全部退出时才会释放锁
class A{
synchroized f(){
t()
}
synchroized t(){
}
}
A:不会。1个线程内, 可以重复进入1个对象的synchroized 块。
原理:当线程请求自己的锁时。JVM会记下锁的持有者,并且给这个锁计数为1。如果该线程再次请求自己的锁,则可以再次进入,计数为2.退出时计数-1. 直到全部退出时才会释放锁
Q: 2个线程同时调用f1和f2会产生同步吗?
class A{
private static synchronized void f1(){};
private synchronized void f2(){};
}
A:不会产生同步。二者不是1个锁。
f1是类锁,等同于synchronized(A.class)
f2是对象锁。
class A{
private static synchronized void f1(){};
private synchronized void f2(){};
}
A:不会产生同步。二者不是1个锁。
f1是类锁,等同于synchronized(A.class)
f2是对象锁。
其他的同步工具
CountDownLatch
final CountDownLatch latch = new CountDownLatch(2);
// 2是计数器初始值
// 2是计数器初始值
初始化, 然后执行latch.await()时, 就会阻塞,直到其他线程中把这个latch进行latch.countDown(),并且计数器降低至0。
和join的区别:
join阻塞时,是只等待单个线程的完成
而CountDownLatch可能是为了等待多个线程
join阻塞时,是只等待单个线程的完成
而CountDownLatch可能是为了等待多个线程
Q: countDownLatch的内部计数值能被重置吗?
A: 不能重置了。如果要重新计数必须重新new一个。毕竟他的类名就叫DownLatch
A: 不能重置了。如果要重新计数必须重新new一个。毕竟他的类名就叫DownLatch
FutureTask
FutureTask<Integer> task = new FutureTask<>(runable);
可以理解为一个支持有返回值的线程,当调用task.get()时,就能能达到线程里的返回值
Q:调用futrueTask.get()时,这个是阻塞方法吗?如果是阻塞,什么时候会结束?
A:是阻塞方法。
A:是阻塞方法。
- 线程跑完并返回结果
- 阻塞时间达到futrueTask.get(xxx)里设定的xxx时间
- 线程出现异常InterruptedException或者ExecutionException
- 线程被取消,抛出CancellationException
Semaphore
Semaphore semaphore = new Semaphore(5, true);
//5表示信号数, true表示公平锁
//5表示信号数, true表示公平锁
用Semaphore(permits)构造一个包含permits个资源的信号量
然后某线程做消费动作, 则执行semaphore.acquire(),则会消费一个资源
如果某线程做生产动作,则执行semaphore.release(),则会释放一个资源(即新增一个资源)
然后某线程做消费动作, 则执行semaphore.acquire(),则会消费一个资源
如果某线程做生产动作,则执行semaphore.release(),则会释放一个资源(即新增一个资源)
Q:信号量中, 公平锁和非公平锁的实现是怎样的?
A:如果当前线程不是锁的占有者,则NonfairSync并不判断是否有等待队列,直接使用compareAndSwap去进行锁的占用,即谁正好抢到,就给谁用!
如果当前线程不是锁的占有者,则FairSync则会判断当前是否有等待队列,如果有则将自己加到等待队列尾,即严格的先到先得!
A:如果当前线程不是锁的占有者,则NonfairSync并不判断是否有等待队列,直接使用compareAndSwap去进行锁的占用,即谁正好抢到,就给谁用!
如果当前线程不是锁的占有者,则FairSync则会判断当前是否有等待队列,如果有则将自己加到等待队列尾,即严格的先到先得!
CyclicBarrier
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, runnable());
//5为子任务数, runable为子任务都阻塞后的执行流程
//流程执行完后, 子任务继续执行
//5为子任务数, runable为子任务都阻塞后的执行流程
//流程执行完后, 子任务继续执行
栅栏,一般是在线程中去调用的
它的构造需要指定1个线程数量,和栅栏被破坏前要执行的操作
每当有1个线程调用barrier.await(),就会进入阻塞,同时barrier里的线程计数-1。
当线程计数为0时, 调用栅栏里指定的那个操作后,然后破坏栅栏, 所有被阻塞在await上的线程继续往下走。
它的构造需要指定1个线程数量,和栅栏被破坏前要执行的操作
每当有1个线程调用barrier.await(),就会进入阻塞,同时barrier里的线程计数-1。
当线程计数为0时, 调用栅栏里指定的那个操作后,然后破坏栅栏, 所有被阻塞在await上的线程继续往下走。
Exchanger
两方栅栏,用于交换数据。
简单说就是一个线程在完成一定的事务后,想与另一个线程交换数据
则第一个先拿出数据的线程会一直等待第二个线程,直到第二个线程拿着数据到来时才能彼此交换对应数据
线程池
Q: ThreadPoolExecutor线程池构造参数中,corePoolSize和maximumPoolSize有什么区别?
A:当提交新线程到池中时, 假设当前线程数为n
调用prestartCoreThread()可提前开启一个空闲的核心线程
调用prestartAllCoreThreads(),可提前创建corePoolSize个核心线程。
A:当提交新线程到池中时, 假设当前线程数为n
- n < corePoolSize,则会创建新线程
- n == corePoolSize & 等待队列未满,则新线程添加等待队列中等待。
- 如果队列也被塞满了,那么又会开始新建线程来运行任务,避免任务阻塞或者丢弃
- 如果队列满了的情况下, 线程总数超过了maxinumPoolSize,那么就抛异常或者阻塞(取决于队列性质)。
调用prestartCoreThread()可提前开启一个空闲的核心线程
调用prestartAllCoreThreads(),可提前创建corePoolSize个核心线程。
Q: 线程池的keepalive参数是干嘛的?
A:当线程数量在corePoolSize到maxinumPoolSize之间时, 如果有线程已跑完,且空闲时间超过keepalive时,则会被清除
(注意只限于corePoolSize到maxinumPoolsize之间的线程)
A:当线程数量在corePoolSize到maxinumPoolSize之间时, 如果有线程已跑完,且空闲时间超过keepalive时,则会被清除
(注意只限于corePoolSize到maxinumPoolsize之间的线程)
Q: 线程池有哪三种队列策略?
A:
A:
- 握手队列
- 无界队列
- 有界队列
Q: submit和execute的区别是什么?
A: execute只能接收Runnable类型的任务,而submit除了Runnable,还能接收Callable(Callable类型任务支持返回值)
execute方法返回void, submit方法返回FutureTask。
异常方面, submit方法因为返回了futureTask对象,而当进行future.get()时,会把线程中的异常抛出,因此调用者可以方便地处理异常。(如果是execute,只能用内部捕捉或者设置catchHandler)
A: execute只能接收Runnable类型的任务,而submit除了Runnable,还能接收Callable(Callable类型任务支持返回值)
execute方法返回void, submit方法返回FutureTask。
异常方面, submit方法因为返回了futureTask对象,而当进行future.get()时,会把线程中的异常抛出,因此调用者可以方便地处理异常。(如果是execute,只能用内部捕捉或者设置catchHandler)
Q:线程池中, shutdown、 shutdownNow、awaitTermination的区别?
A:
A:
- shutdown: 停止接收新任务,等待所有池中已存在任务完成( 包括等待队列中的线程 )。异步方法,即调用后马上返回。
- shutdownNow: 停止接收新任务,并 停止所有正执行的task,返回还在队列中的task列表 。
- awaitTermination: 仅仅是一个判断方法,判断当前线程池任务是否全部结束。一般用在shutdown后面,因为shutdown是异步方法,你需要阻塞等待什么时候才真正结束。
ThreadLocal
Q: ThreadLocal的常见使用场景?
A:
每个线程中需要维护1个不同的副本, 但这个副本可能是某一个时刻一起塞入每个线程的, 只不过之后该副本的变化 不再受其他线程的影响。
常见场景有连接器管理模块connectorManager, 每个线程持有的connect变量是单独使用的,不会互相影响或者需要加锁。原因就是将其作为副本放入每个线程,当线程启动连接或者关闭时,不影响其他线程里的getConnect方法
A:
每个线程中需要维护1个不同的副本, 但这个副本可能是某一个时刻一起塞入每个线程的, 只不过之后该副本的变化 不再受其他线程的影响。
常见场景有连接器管理模块connectorManager, 每个线程持有的connect变量是单独使用的,不会互相影响或者需要加锁。原因就是将其作为副本放入每个线程,当线程启动连接或者关闭时,不影响其他线程里的getConnect方法
Q: ThreadLocal和Synchronized关键字的区别?
A:
Synchronized是用时间的消耗,来换取数据同步以及互不冲突
ThreadLocal则是用空间的消耗,来换取数据之间互不冲突(不涉及同步)
A:
Synchronized是用时间的消耗,来换取数据同步以及互不冲突
ThreadLocal则是用空间的消耗,来换取数据之间互不冲突(不涉及同步)
Q:TheadLocal在每个线程中是以什么形式存储的? 原理是什么
A:在某个线程中调用 某threadlocal.set(value)时, 其实就是在该线程中新建了1个threalocalMap, 然后把threadLocal作为键,value作为值,放进本线程的threalocalMap中。
当在线程中调用threadlocal.get()的时候,就是从线程的threadLocalMap中获取这个threadLocal对应的值. 如果get不到,则可以通过自定义initValue方法生成一个threadLocal的默认值
A:在某个线程中调用 某threadlocal.set(value)时, 其实就是在该线程中新建了1个threalocalMap, 然后把threadLocal作为键,value作为值,放进本线程的threalocalMap中。
当在线程中调用threadlocal.get()的时候,就是从线程的threadLocalMap中获取这个threadLocal对应的值. 如果get不到,则可以通过自定义initValue方法生成一个threadLocal的默认值
0 条评论
下一页