1.什么是线程 ?线程和进程的区别 ?
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。而进程是系统中
正在运行的一个程序,程序一旦运行就是进程。
区别:(1)线程是 CPU 调度的基本单位,没有独立的地址空间;进程是资源分配的基本单位,有独立的地址空
间。
(2)进程间切换代价大,线程间切换代价小。
(3)进程拥有的资源多,线程拥有的资源少。
(4)一个进程无法直接访问另一个进程的资源,同一进程内的多个线程可以共享该进程的资源。
(5)线程属于进程,不能独立执行,每一个进程至少要有一个线程,并成为主线程。
2.描述 CPU 和多线程的关系 ?
(1)第一阶段:单CPU时代,单CPU在同一时间点,只能执行单一线程。
(2)第二阶段:单CPU多任务阶段,计算机在同一时间点,并行执行多个线程。但这并非真正意义上的同时执
行,而是多个任务共享一个CPU,操作系统协调 CPU 在某个时间点执行某个线程,因为CPU在线程之间切换比较
快,给人的感觉就好像是多个任务在同时运行。
(3)第三阶段:多 CPU 多任务阶段,真正实现了在同一时间点运行多个线程。
3.什么是线程安全和线程不安全 ?
(1)线程安全:指多个线程在执行同一段代码的时候采用加锁机制,使每次的执行结果和单线程执行的结果都是
一样的,不存在执行程序时出现意外结果。
(2)线程不安全:指不提供加锁保护机制,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。
4.描述线程的生命周期 ?(画图)
线程的生命周期包括五个阶段:创建、就绪、运行、阻塞、销毁。
(1)创建:用 new 关键字建立一个线程后,该线程对象就处于创建状态,处于新生状态的线程有自己的内存空
间,通过调用 start() 方法进入就绪状态。
(2)就绪:调用线程的 start() 方法后,线程进入就绪状态,这时候线程处于等待 CPU 分配资源阶段,谁先抢的
CPU资源,谁开始执行。
(3)运行:当就绪的线程被调度并获得 CPU 资源时,便进入运行状态,run() 方法定义了线程的操作和功能。
(4)阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如 sleep()、wait() 之
后线程就处于了阻塞状态,这个时候需要其它机制将处于阻塞状态的线程唤醒,比如调用 notify() 或者 notifyAll()
方法,唤醒的线程不会立刻执行 run() 方法,他们要再次等待 CPU 分配资源进入运行状态。
(5)销毁:如果线程正常执行完毕后或线程被提前强制性的终止或者出现异常导致结束,那么线程就要被销毁,
同时释放资源。
5.wait、sleep、join、yield 的区别 ?
(1)wait:wait 方法是属于 Object 类中的,wait 过程中线程会释放对象锁,只有当其它线程调用 notify 时才能
唤醒此线程。wait 使用时必须先获取对象锁,即必须在 Synchronized 修饰的代码块中使用,那么相应的 notify
方法同样也必须在 Synchronized 修饰的代码块中使用,如果没有在 Synchronized 修饰的代码块中使用,那么在
运行时就会抛出 IllegalMonitorStateException 异常。
(2)sleep:在指定时间内让当前正在执行的线程暂停执行,sleep 过程中线程不会释放锁,只会阻塞线程,当到
了指定的时间后才会自动恢复运行状态。
(3)join:等待调用 join 方法的线程结束之后,程序将会继续执行。
(4)yield:暂停当前正在执行的线程对象,yield 过程中不会释放资源锁,和 sleep 不同的是 yield 方法并不会让
线程进入阻塞状态,而是让线程重回就绪状态,允许其它具有相同优先同级的线程获得运行的机会。
6.Synchronized 和 Lock 的区别 ?
(1)Lock 是一个接口,而 Synchronized是 java 的一个关键字。
(1)Lock 有比 Synchronized 更精确的线程语义和更好的性能。
(2)Synchronized 会自动释放锁,从而可以避免死锁,而 Lock 一定要求程序员手工释放,可能引起死锁,并且
必须在 finally 从句中释放。
7.ThreadLocal、Volatile、Synchronized 的作用和区别 ?
(1)ThreadLocal 不是为了解决多线程访问共享变量,而是为每个线程创建一个单独的变量副本,提供了保持对
象的方法和避免参数传递的复杂性。
(2)Volatile 主要是用来在多线程中同步变量。
(3)Synchronized 关键字保证了数据读写一致和可见性等问题。
8.同步方法和同步代码块哪个更好 ?
(1)同步代码块是更好的选择,因为它不会锁住整个对象,而同步方法会锁住整个对象,哪怕这个类中有多个不
相关联的同步代码块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。
(2)同步代码块更符合开发调用的原则,只要在需要锁住的代码块中锁住相应的对象,这样从侧面来说也可以避
免死锁。
-
同步方法使用 Synchronized 修饰方法,在调用该方法前,需要获得内置锁(java每个对象都有一个内置
锁),否则就处于阻塞状态。
-
同步代码块使用 Synchronized(object){} 进行修饰,在调用该代码块时,需要获得内置锁,否则就处于阻塞状
态。
9.什么是死锁 ?如何避免死锁 ?
线程死锁:是指由于两个或者多个线程竞争资源或者互相持有对方所需要的资源,导致这些线程处于等待状态,无
法前往执行,当两个线程相互等待对方释放资源时,就会发生死锁。
(1)避免多次锁定。
(2)具有相同的加锁顺序。
(3)使用定时锁。
(5)死锁检测。
10.常见的线程池叫什么 ?线程池的作用是什么 ?
常见的线程池有:FixedThreadPool、CachedThreadPool、SingleThreadPool、ScheduledThreadPool。
作用:(1)减少在创建和销毁线程上所花的时间以及系统资源的开销 。
(2)如果不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及 ”过度切换”。