Java多线程面试题(三)
- 前言
- 1、Thread 类中的 yield 方法有什么作用?
- 2、Java 线程池中 submit() 和 execute()方法有什么区别?
- 3、Java 中的 ReadWriteLock 是什么?
- 4、可以直接调用 Thread 类的 run ()方法么?
- 5、如何让正在运行的线程暂停一段时间?
- 6、如何确保 main()方法所在的线程是 Java 程序最后结束的线程?
- 7、线程之间是如何通信的?
- 8、为什么 wait(), notify()和 notifyAll ()必须在同步方法或者同步块中被调用?
- 9、为什么 Thread 类的 sleep()和 yield ()方法是静态的?
- 10、如何确保线程安全?
- 11、同步方法和同步代码块,哪个是更好的选择?
- 12、如何创建守护线程?
- 13、什么是 Java Timer 类?如何创建一个有特定时间间隔的任务?
- 14、并发编程三要素?
- 15、实现可见性的方法有哪些?
- 16、多线程的价值?
- 17、什么是 AQS?
- 18、synchronized、volatile、CAS 比较?
- 19、同步方法和同步块,哪个是更好的选择?
- 20、Java 线程数过多会造成什么异常?
- 总结
前言
最新的 Java 面试题,技术栈涉及 Java 基础、集合、多线程、Mysql、分布式、Spring全家桶、MyBatis、Dubbo、缓存、消息队列、Linux…等等,会持续更新。
如果对老铁有帮助,帮忙免费点个赞,谢谢你的发财手!
1、Thread 类中的 yield 方法有什么作用?
使当前线程从运行状态变为就绪状态。
如果线程到了就绪状态,可能是立即调用,也可能继续等待 。
2、Java 线程池中 submit() 和 execute()方法有什么区别?
两个方法都可以向线程池提交任务。
- execute()方法的返回类型是 void,它定义在Executor 接口中。
- submit()方法可以返回持有计算结果的 Future 对象,它定义在ExecutorService 接口中,扩展了 Executor 接口。
3、Java 中的 ReadWriteLock 是什么?
实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。
4、可以直接调用 Thread 类的 run ()方法么?
当然可以。但是如果我们调用了 Thread 的 run()方法,它的行为就会和普通的方法一样,会在当前线程中执行。为了在新的线程中执行我们的代码,必须使用Thread.start()方法。
5、如何让正在运行的线程暂停一段时间?
我们可以使用 Thread 类的 Sleep()方法让线程暂停一段时间。需要注意的是,这并不会让线程终止,一旦从休眠中唤醒线程,线程的状态将会被改变为 Runnable,并且根据线程调度,它将得到执行。
6、如何确保 main()方法所在的线程是 Java 程序最后结束的线程?
我们可以使用 Thread 类的 join()方法来确保所有程序创建的线程在 main()方法退出前结束。
7、线程之间是如何通信的?
当线程间是可以共享资源时,线程间通信是协调它们的重要的手段。Object 类中wait()、notify()、notifyAll()方法都可以用于线程间通信。
8、为什么 wait(), notify()和 notifyAll ()必须在同步方法或者同步块中被调用?
当一个线程需要调用对象的 wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态,直到其他线程调用这个对象上的 notify() 方法。
由于所有的这些方法都需要线程持有对象的锁,这样就只能通过同步来实现,所以他们只能在同步方法或者同步块中被调用。
9、为什么 Thread 类的 sleep()和 yield ()方法是静态的?
Thread 类的 sleep()和 yield()方法将在当前正在执行的线程上运行。
所以在其他处于等待状态的线程上调用这些方法是没有意义的。这就是为什么这些方法是静态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。
10、如何确保线程安全?
在 Java 中可以有很多方法来保证线程安全——同步,使用原子类(atomic concurrent classes),实现并发锁,使用 volatile 关键字,使用不变类和线程安全类。
11、同步方法和同步代码块,哪个是更好的选择?
同步代码块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。
同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,也会导致他们停止执行并需要等待获得这个对象上的锁,这样从侧面来说同步代码块可以避免死锁。
12、如何创建守护线程?
使用 Thread 类的 setDaemon(true)方法可以将线程设置为守护线程,需要注意的是,需要在调用 start()方法前调用这个方法,否则会抛出IllegalThreadStateException 异常。
13、什么是 Java Timer 类?如何创建一个有特定时间间隔的任务?
- java.util.Timer 是一个工具类,可以用于安排一个线程在未来的某个特定时间执行。
Timer 类可以用安排一次性任务或者周期任务。 - java.util.TimerTask 是一个实现了 Runnable 接口的抽象类,我们需要去继承这个类来创建我们自己的定时任务并使用 Timer 去安排它的执行
14、并发编程三要素?
- 1、原子性
原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行。 - 2、可见性
可见性指多个线程操作一个共享变量时,其中一个线程对变量进行修改后,其他线程可以立即看到修改的结果。 - 3、有序性
有序性,即程序的执行顺序按照代码的先后顺序来执行。
15、实现可见性的方法有哪些?
synchronized 或者 Lock:保证同一个时刻只有一个线程获取锁执行代码,锁释放之前把最新的值刷新到主内存,实现可见性。
16、多线程的价值?
- 1、发挥多核 CPU 的优势
多线程,可以真正发挥出多核 CPU 的优势来,达到充分利用 CPU 的目的,采用多线程的方式去同时完成几件事情而不互相干扰。 - 2、防止阻塞
如果使用单线程,那么只要这个线程阻塞了,那么你的整个程序就停止运行了。
多线程可以防止这个问题,多条线程同时运行,也不会影响其它任务的执行。
17、什么是 AQS?
AQS是一个抽象队列式同步器,它有两个重要的东西,一个以Node为节点实现的链表的队列(CHL队列),还有一个volatile修饰的int类型的state标志,并且通过CAS来改变它的值,0是无锁状态,1是上锁状态。
多线程竞争资源时,通过CAS的方式来修改state。
AQS支持线程抢占两种锁——独占锁和共享锁:
- 独占锁:ReentrantLock、ReentrantReadWriteLock.WriteLock,独占锁需要实现的是 tryAcquire(int)、tryRelease(int)。
- 共享锁:ReentrantReadWriteLock.ReadLock、Semaphore、CountDownLatch、CyclicBarrier,共享锁需要实现的是 tryAcquireShared(int)、tryReleaseShared(int)。
18、synchronized、volatile、CAS 比较?
- 1、synchronized 是悲观锁,属于抢占式,会引起其他线程阻塞。
- 2、volatile 提供多线程共享变量可见性和禁止指令重排序优化。
- 3、CAS 是基于冲突检测的乐观锁(非阻塞)。
19、同步方法和同步块,哪个是更好的选择?
同步块,这意味着同步块之外的代码是异步执行的,这比同步整个方法更提升代码的效率。
请知道一条原则:同步的范围越小越好。
20、Java 线程数过多会造成什么异常?
- 1、线程的生命周期开销非常高
- 2、消耗过多的 CPU 资源
如果可运行的线程数量多于可用处理器的数量,那么有线程将会被闲置。大量空闲的线程会占用许多内存,给垃圾回收器带来压力,而且大量的线程在竞争 CPU 资源时还将产生其他性能的开销。 - 3、降低稳定性
JVM 在可创建线程的数量上存在一个限制,这个限制值承受着多个因素制约,包括 JVM 的启动参数、Thread 构造函数中请求栈的大小,以及底层操作系统对线程的限制等。如果破坏了这些限制,那么可能抛出OutOfMemoryError 异常。
总结
都已经看到这里啦,赶紧收藏起来,祝您工作顺心,生活愉快!