2023年Java面试题-多线程

系列文章目录

2023年Java面试题


目录

  • 系列文章目录
  • 多线程
  • 一、串行,并行,和并发的区别?
  • 二、守护线程是什么?
  • 三、创建线程有哪几种方式?
  • 四、线程有哪些状态?
  • 五、sleep() 和 wait() 有什么区别?
  • 六、notify()和 notifyAll()有什么区别?
  • 七、线程的 run() 和 start() 有什么区别?
  • 八、怎么保证多线程的运行安全?
  • 九、什么是死锁?
  • 十、怎么防止死锁?
  • 十一、synchronized 和 Lock 有什么区别?
  • 十二、用Runnable还是Thread好?
  • 十三、什么是线程池? 为什么要使用它?
  • 十四、怎么确保线程按顺序执行?
  • 十五、yield方法有什么作用?
  • 十六、如果你提交任务时,线程池队列已满。会发生什么?
  • 十七、什么是阻塞式方法?
  • 十八、如果同步代码块内的线程抛出异常会发生什么?
  • 十九、悲观锁
  • 二十、乐观锁
  • 二十一、线程池都有哪些状态?


多线程


一、串行,并行,和并发的区别?

串行:是指如果有多个任务需要执行,先执行一个任务,等这个任务完成后,再执行另一个任务,两个任务执行的时间不会发生重叠。
并行:多个任务在同一时刻互不干扰的同时执行。
并发:多个任务在同一个 CPU 上,按细分的时间片轮流(交替)执行。

二、守护线程是什么?

守护线程就是服务线程,准确地来说就是服务其他线程的线程,比如垃圾回收器就是特殊的守护线程。

三、创建线程有哪几种方式?

继承 Thread	类。
实现 Runnable 接口。
实现 Callable 接口。

四、线程有哪些状态?

新建状态(NEW)
	new一个线程出来,这个线程就进入了新建状态
可运行状态(RUNNABLE)
	就绪状态(READY)
		调用线程的start()方法,这个状态的线程位于可运行线程池中,等待被调度线程选中,获取CPU的使用权限,此时就处于就绪状态。
	运行状态(RUNNING)
		就绪状态的线程在获得CPU时间片后就变为运行状态
阻塞状态(BLOCKED)
	阻塞状态是线程阻塞在进入synchronized修饰的方法或代码块(获取锁)时的状态。
等待状态(WAITING)
	处于等待状态的线程不会被分配CPU时间片,它们需要等待被其他线程唤醒,否则会处于无限期等待的状态。
限时等待状态(TIMED_WAITING)
	处于限时等待状态的线程也不会被分配CPU时间片,不过不用无限期等待被其他线程唤醒,在达到一定时间后它们会自动唤醒。
终止状态(TERMINATED)
	线程执行完成或者被中断,就进入终止状态。进入终止状态的线程,就没办法重新启动了。如果再次调用 start() 方法,
	就会抛出 IllegalThreadStateException 异常。就算调用run() 方法也不会有任何效果。

五、sleep() 和 wait() 有什么区别?

首先是类的不同:sleep() 来自 Threadwait() 来自 Object。
然后是释放锁的不同:sleep() 不释放锁;wait() 会释放锁。
再有就是唤醒的不同:sleep() 会自动唤醒;而wait() 是需要使用 notify()或者notifyAll()唤醒的。

六、notify()和 notifyAll()有什么区别?

notifyAll()会唤醒所有等待的线程,会把所有线程从等待池移动到锁池,然后参与锁的竞争,
竞争成功就继续执行,如果不成功就留在锁池等待锁被释放后再次参与竞争。而 notify()只会唤醒一个线程,
具体唤醒哪一个线程是由实现的Java虚拟机控制的,有可能是随机唤醒,也有可能是按“先进先出”的顺序唤醒。

七、线程的 run() 和 start() 有什么区别?

调用start方法可以启动线程,而且会自动调用 run()方法。而run方法只是thread的一个普通方法,如果直接调用Run方法,还是在主线程里顺序执行,这样就达不到多线程的目的了。

八、怎么保证多线程的运行安全?

使用 synchronized关键字(同步方法或者同步代码块)
使用 Lock接口的实现类(ReentrantLock)
分布式锁(数据库分布式锁,zookeeper分布式锁,redis分布式锁等)

九、什么是死锁?

比如有多个线程要去访问同一块资源,线程1拿到了A锁,线程2拿到了B锁,2个线程各拿到了1把锁,线程1在等线程2的锁,线程2在等线程1的锁,谁都没有办法继续往下执行,出现互相等待的情况,这种情况就是死锁。

十、怎么防止死锁?

避免嵌套锁:这是死锁出现的最常见原因,如果已经持有一个资源,就需要避免锁定另外一个资源。假如只使用一个对象锁的话,其实就不会出现死锁情况
只锁需要的部分:就是只对需要的资源加锁
避免无限期等待:在线程尝试获取锁的时候加上一个超时时间,超过这个时间就放弃对这个锁的请求,并且释放掉自己占有的锁

十一、synchronized 和 Lock 有什么区别?

synchronized关键字,Lock是个接口。
synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
synchronized无法判断是否获取到锁,Lock可以判断是否获取到锁。
synchronized会自动释放锁,Lock需在finally中手动释放锁(unlock()方法释放锁),否则容易造成死锁。

十二、用Runnable还是Thread好?

如果需要继承其他类,当然是实现Runnable接口好了,可以避免Java中的单继承限制。

十三、什么是线程池? 为什么要使用它?

线程池就是事先创建一些可执行的线程放入一个容器(池)中,需要用的时候从容器里面获取,用完以后不需要销毁线程而是把线程放回容器中,从而去减少创建线程和销毁线程的开销。
让每个线程可以多次使用,还可以根据系统情况调整执行的线程数量,防止消耗过多内存。

十四、怎么确保线程按顺序执行?

可以用Threadjoin()方法,它的作用是让父线程必须等待子线程执行结束之后才能继续运行。
也可以使用单线程的线程池,这样的话内部的线程就会按照加入的顺序来执行。

十五、yield方法有什么作用?

可以让当前线程从运行状态变为就绪状态。

十六、如果你提交任务时,线程池队列已满。会发生什么?

许多程序员会认为这个任务会一直阻塞,直到线程池队列有空位为止。但事实上如果一个任务不能被调度执行那么线程池会抛出一个RejectedExecutionException(拒绝执行)异常。

十七、什么是阻塞式方法?

java中的阻塞式方法是指,在调用方法时,必须等待方法执行完成或者抛出异常,否则程序会一直停留在这个语句上面,
不会继续执行下面的语句。比如多线程中sleep()方法,就是一个阻塞式方法。

十八、如果同步代码块内的线程抛出异常会发生什么?

无论同步代码块里的线程是正常退出还是异常退出的,里面的线程都会释放锁。

十九、悲观锁

悲观锁又叫互斥同步锁,总是假设最坏的情况,每次去拿数据都认为别人会修改,所以每次都会上锁,
当其他线程来访问的时候,就会进入阻塞状态,从而确保数据的安全性。
(悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了)

二十、乐观锁

乐观锁是一种不会阻塞其他线程并发的机制,总是假设最好的情况,每次去拿数据都认为别人不会修改,所以每次都不会上锁,
只在更新的时候会判断一下在此期间有没有人去修改这个数据。
如果没有,那就会正常执行;如果有,那为了保证数据的安全性,就放弃修改或者报错。
(数据库的乐观锁需要自己实现,在表里面添加一个 version 字段,每次修改成功值加 1,
这样每次修改的时候先对比一下,自己拥有的 version 和数据库现在的 version 是否一致,如果不一致就不修改,这样就实现了乐观锁)

二十一、线程池都有哪些状态?

运行状态(RUNNING)
	线程池创建好之后就会进入运行状态
关闭状态(SHUTDOWN)
	不再接受新任务提交,但是会把任务队列中已有的任务处理完。
停止状态(STOP)
	不再接受新任务提交,并且会中断当前正在执行的任务、放弃任务队列中已有的任务。
整理状态(TIDYING)
	所有的任务都执行完毕后,就会进入整理状态,进入整理状态之后,就会调用线程池的结束( terminated )方法。
终止状态(TERMINATED)
	当执行完线程池的 结束(terminated) 方法之后就会变为终止状态。

你可能感兴趣的:(Java,java,开发语言)