面试题

并行和并发有什么区别?
  • 并行是指多个事件在同一时间发生
  • 并发是指多个事件在同一时间间隔发生
  • 简单点说就是并行是指多个人做多件事情,并发是一个人做多件事情
线程和进程的区别?
  • 进程是程序运行的最小单位
  • 线程是CPU调度的最小单位
  • 一个进程内至少含有一个线程
守护线程是什么?
  • 守护线程是一个服务于其它线程的线程,例如GC线程
创建线程有哪几种方式?
  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口
  • 通过线程池创建
说一下 runnable 和 callable 有什么区别?
  • Runnable实现的多线程无返回值,不可抛出异常
  • Callable实现的多线程可有返回值,可抛出异常
线程有哪些状态?
  • 新建:创建了一个线程对象,但是还没有调用start()方法
  • 就绪:调用start()方法后,线程还没有获取到CPU资源,此时是就绪状态,
  • 运行:当线程获取到CPU资源时就进入运行状态
  • 阻塞:线程在运行的时候被暂停了,例如调用join()(当join的线程执行完成后解除阻塞)、wait()(notify()、notifyAll()方法可以解除阻塞)、suspend()(resume())、sleep()(睡眠时间结束后自动结束阻塞)等方法都会让线程进入阻塞状态。
  • 死亡:线程运行完了就死亡了,死亡的线程不可再次启动
  • 线程状态转换图.png
sleep() 和 wait() 有什么区别?
  • sleep()是Thread类的一个静态方法;wait()是Object类的一个方法
  • sleep()和wait()都可以让当前线程进入阻塞状态,sleep()随着时间结束会自动结束阻塞状态;wait()需要通过其它线程调用notify()或notifyAll()时才会结束阻塞
  • sleep()可以在任何地方调用,wait()只能在同步代码块或同步方法中调用,调用对象为对应的同步监视器
  • 调用sleep()不会释放锁。调用wait()会释放当前持有的锁
notify()和 notifyAll()有什么区别?
  • notify()会解除某一个被wait()阻塞的线程,通常是优先级高的线程
  • notifyAll()会解除所有被wait()阻塞的线程
线程的 run()和 start()有什么区别?
  • 当调用run()方法只是同步的执行对应的方法
  • 调用start()方法会启动一个线程,然后执行对应的run()方法
创建线程池有哪几种方式?
线程池都有哪些状态?
线程池中 submit()和 execute()方法有什么区别?
  • submit()有返回值
  • execute()方法无返回值
在 java 程序中怎么保证多线程的运行安全?
  • 同步代码块
  • 同步方法
多线程锁的升级原理是什么?
什么是死锁?
  • 当多个线程持有对方需要的资源,同时又在等待对方释放对应的资源,导致程序停止运行。
怎么防止死锁?
ThreadLocal 是什么?有哪些使用场景?
说一下 synchronized 底层实现原理?
  • Java中每一个对象都可以作为锁或者说是同步监视器
  • 只有获取到同步监视器的线程才能够执行对应的同步代码块或同步方法
  • 当线程执行完对应的同步方法或同步代码块时会释放对应的同步监视器,此时其它线程就可以再次去获取这个同步监视器获取执行代码的权限
synchronized 和 volatile 的区别是什么?
synchronized 和 Lock 有什么区别?
  • synchronized是一个关键词,Lock是一个接口
  • synchronized获取锁和释放锁都是自动完成的,Lock需要手动上锁和解锁
  • Lock可以获取锁的状态
  • Lock只能锁代码块,不能锁方法。synchronized可以用在方法上
synchronized 和 ReentrantLock 区别是什么?
说一下 atomic 的原理?

今日总结

程序

是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。

进程

是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:它有自身的产生、存在和消亡的过程。进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域。

线程

  • 进程可进一步细化为线程,是程序内部的一条执行路径。
  • 若一个进程同一时间并行执行多个线程,就是支持多线程的
  • 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器,线程切换的开销小
  • 一个进程中的多个线程共享相同的内存单元/内存地址空间-->他们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、搞笑。但多个线程共享的系统资源可能就会带来安全隐患。
  • (虚拟机栈、程序计数器每个线程独立一份)
  • (方法区、堆一个进程一份,多个进程共享)

多线程的优点

  • 提高应用程序的响应。
  • 提高计算机CPU的利用率
  • 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改。

什么时候需要多线程?

  • 程序需要同时执行两个或多个任务
  • 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
  • 需要一些后台运行的程序时

创建线程的方式

  • 继承Thread类
  • 实现Runnable接口:没有单继承的局限性、更适合处理多线程共享数据的情况
  • 实现Callable接口。 --- JDK 5.0新增(有返回值、可抛出异常、支持泛型)
  • 使用线程池创建

Thread类的有关方法

  • start(): 启动线程,并执行对象的run方法
  • run(): 线程在被调度时执行的操作
  • getName(): 返回线程的名称
  • setName(): 设置线程名称
  • currentThread(): 返回当前线程
  • yield(): 释放当前CPU的执行权
  • join(): 在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完成后,线程a才结束阻塞状态
  • sleep(long mills): 让当前线程阻塞指定时间
  • isAlive(): 判断当前线程是否存活
  • stop(): 强制线程生命期结束

线程的生命周期

  • 新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。
  • ** 就绪:** 处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是还没分配到CPU资源。
  • 运行: 当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能
  • 阻塞: 在某种特殊情况下,被人挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态
  • 死亡: 线程完成了它的全部工作或线程被提前强制性中止或出现异常导致结束
    线程状态转换图.png

当多个线程共享数据时,会存在线程安全问题。在Java中通过同步机制,来解决线程的安全问题。

  • 同步代码块
public void test(){
	// 操作共享数据的代码即为需要被同步的代码。
	// 同步监视器:俗称锁。任何一个类的对象,都可以充当锁。
	// 多个线程必须共用同一把锁
	synchronized(同步监视器) {
		// 需要被同步的代码
	}
} 
  • 同步方法
// 静态同步方法:默认使用当前类对象作为同步监视器
// 非静态同步方法:默认使用当前对象(this)作为同步监视器
  • Lock锁---JDK5.0新增的方式

同步原理

Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到内核态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因。

线程的死锁问题

  • 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
  • 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续

synchronized与lock的区别

  • Lock是一个接口,而synchronized是关键字。
  • synchronized会自动释放锁,而Lock必须手动释放锁。
  • Lock可以让等待锁的线程响应中断,而synchronized不会,线程会一直等待下去。
  • 通过Lock可以知道线程有没有拿到锁,而synchronized不能。
  • Lock能提高多个线程读操作的效率。
  • synchronized能锁住类、方法和代码块,而Lock是块范围内的

线程的通信

  • wait(): 一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器
  • notify(): 一旦执行此方法,就会唤醒被wait()的一个线程。如果有多个线程被wait(),就唤醒优先级高的线程
  • notifyAll(): 一旦执行次方法,就会唤醒所有被wait()阻塞的线程
  • wait(),notify(),notifyAll()必须使用在同步代码块或同步方法中。
  • wait(),notify(),notifyAll()这三个方法的调用者必须是同步代码块或同步方法中的同步监视器;否则会出现异常
  • wait(),notify(),notifyAll()这三个方法是定义在java.lang.Object类中。同步监视器可以是任何对象,因此将这三个方法定义在Object类中

sleept()和wait()的区别

  • 两个方法声明的位置不同,sleep是在Thread类中声明的,wait方法在Object类中声明的。
  • sleep可以在任何场景中使用,wait只能在同步代码中使用
  • sleep不会释放同步监视器,wait会释放同步监视器