多线程经典问
1.1 线程和进程的区别?
进程是正在运行的程序实例,也是资源分配的基本单位;线程是进程中的执行单元,是 CPU 调度的基本单位。
不同进程之间内存隔离,同一进程内的线程共享堆、方法区等资源,但每个线程有自己的栈和程序计数器。
线程比进程更轻量,上下文切换成本更低。
1.2 并行和并发有什么区别?
并发:一个CPU核心交替执行多个线程
并行:多个CPU核心各自执行自己的线程
1.3 创建线程的四种方式
在java中一共有四种常见的创建方式,分别是:继承Thread类、实现runnable接口、实现Callable接口、线程池创建线程。通常情况下,我们项目中都会采用线程池的方式创建线程。
1.4 runnable 和 callable 有什么区别?
Runnable 接口run方法没有返回值;Callable接口call方法有返回值,是个泛型,和Future、FutureTask配合可以用来获取异步执行的结果
Callalbe接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞当前调用 get() 的线程
Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛
1.5 线程的 run()和 start()有什么区别?
核心区别->run里是啥,start是啥->run的另一种情况->run重复 start一次run()和start()最大的区别是:start()才是真正启动一个新线程,直接调用run()只是普通方法调用。
run()里面放的是线程要执行的业务逻辑。如果我们调用thread.start(),JVM 会创建一个新的线程,让这个新线程在被 CPU 调度后去执行run()方法。
但如果直接调用thread.run(),它不会创建新线程,而是在当前线程里同步执行,就和调用一个普通方法一样。
另外,同一个线程对象的start()只能调用一次,重复调用会抛IllegalThreadStateException;但run()作为普通方法可以被多次调用。
1.6 线程包括哪些状态,状态之间是如何变化的?
JDK 里线程状态在Thread.State里定义了 6 种:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。
一个线程刚创建、还没调用start()的时候是 NEW;调用start()之后进入 RUNNABLE。这里的 RUNNABLE 不一定代表正在执行,也可能是在等 CPU 调度。线程的run()方法执行完之后,就进入 TERMINATED。
如果线程在进入synchronized代码块时竞争锁失败,会进入 BLOCKED,等锁释放后再重新竞争,竞争到锁后回到可运行状态。
如果线程调用wait()、join()这类方法,会进入 WAITING;如果调用sleep(long)、wait(long)、join(long),会进入 TIMED_WAITING。区别是wait()会释放锁,需要 notify 或 notifyAll 唤醒后再重新竞争锁;而sleep()只是让线程睡一段时间,不会释放锁。
1.7 新建 T1、T2、T3 三个线程,如何保证它们按顺序执行?
可以用join()。join()表示当前线程等待目标线程执行完。
在主线程里先t1.start(),然后t1.join();等 T1 完成后再t2.start()、t2.join();最后启动 T3。这样就能保证 T1、T2、T3 按顺序执行。
1.8 notify()和 notifyAll()有什么区别?
notifyAll:唤醒所有wait的线程
notify:只随机唤醒一个 wait 线程
1.9 在 java 中 wait 和 sleep 方法的不同?
wait()和sleep()都能让当前线程暂时不执行,都会让出 CPU,也都可以被interrupt()打断。
主要区别有三个。第一,方法归属不一样,sleep()是Thread的静态方法,wait()是Object的方法。
第二,锁的行为不一样。sleep()不要求先拿锁,如果它在synchronized里执行,也不会释放锁;而wait()必须在同步代码块里调用,也就是必须先拿到对象锁,并且调用后会释放这个锁。
第三,唤醒方式不一样。sleep(long)时间到了就会进入可运行状态;wait()需要被notify()或notifyAll()唤醒,wait(long)也可以超时醒。不过wait()被唤醒后不是马上执行,还要重新竞争锁。
所以简单说,sleep()更像是线程自己暂停一下,wait()更像是线程之间配合通信。
1.10 如何停止一个正在运行的线程?
Java 里不推荐用stop()停线程,因为它会强制终止,可能导致锁和共享数据状态不一致。
推荐做法是让线程自己退出,比如用volatile标志位控制循环,或者调用interrupt()设置中断标记。
但interrupt()不是直接停止线程,如果线程在sleep/wait/join中会抛InterruptedException;如果线程正常运行,就需要自己检查isInterrupted()后退出。
