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