大厂P7 Java程序员高频面试题-4

Posted 四猿外

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大厂P7 Java程序员高频面试题-4相关的知识,希望对你有一定的参考价值。

什么是不可变对象,它对写并发应用有什么帮助?

不可变对象(Immutable Objects)即对象一旦被创建它的状态(对象的数据,也即对象属性值)就不能改变,反之即为可变对象(Mutable Objects)。

不可变对象的类即为不可变类(Immutable Class)。Java 平台类库中包含许多不可变类,如String、基本类型的包装类、BigInteger 和BigDecimal 等。

不可变对象天生是线程安全的。它们的常量( 域) 是在构造函数中创建的。既然它们的状态无法修改,这些常量永远不会变。

不可变对象永远是线程安全的。
只有满足如下状态,一个对象才是不可变的:
它的状态不能在创建后再被修改;所有域都是final 类型;并且,它被正确创建(创建期间没有发生this 引用的逸出) 。

什么是多线程中的上下文切换?

在上下文切换过程中, CPU 会停止处理当前运行的程序,并保存当前程序运行的具体位置以便之后继续运行。从这个角度来看,上下文切换有点像我们同时阅读几本书,在来回切换书本的同时我们需要记住每本书当前读到的页码。在程序中,上下文切换过程中的“ 页码”信息是保存在进程控制块( PCB)中的。PCB 还经常被称作“切换桢”( switchframe)。“页码” 信息会一直保存到CPU 的内存中, 直到他们被再次使用。

上下文切换是存储和恢复CPU 状态的过程,它使得线程执行能够从中断点恢复执行。上下文切换是多任务操作系统和多线程环境的基本特征。

Java 中用到的线程调度算法是什么?

计算机通常只有一个CPU,在任意时刻只能执行一条机器指令,每个线程只有获得CPU 的使用权才能执行指令.所谓多线程的并发运行,其实是指从宏观上看,各个线程轮流获得CPU 的使用权,分别执行各自的任务.在运行池中,会有多个处于就绪状态的线程在等待CPU,JAVA 虚拟机的一项任务就是负责线程的调度,线程调度是指按照特定机制为多个线程分配CPU 的使用权.

有两种调度模型:分时调度模型和抢占式调度模型。

分时调度模型是指让所有的线程轮流获得cpu 的使用权,并且平均分配每个线程占用的CPU 的时间片这个也比较好理解。

java 虚拟机采用抢占式调度模型,是指优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。处于运行状态的线程会一直运行, 直至它不得不放弃CPU。

什么是线程组,为什么在Java 中不推荐使用?

线程组和线程池是两个不同的概念, 他们的作用完全不同, 前者是为了方便线程的管理,后者是为了管理线程的生命周期,复用线程,减少创建销毁线程的开销。

为什么使用Executor 框架比使用应用创建和管理线程好?

为什么要使用Executor 线程池框架

1、每次执行任务创建线程new Thread()比较消耗性能,创建一个线程是比较耗时、耗资源的。
2、调用new Thread()创建的线程缺乏管理, 被称为野线程,而且可以无限制的创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪,还有线程之间的频繁交替也会消耗很多系统资源。
3、直接使用new Thread() 启动的线程不利于扩展,比如定时执行、定期执行、定时定期执行、线程中断等都不便实现。

使用Executor 线程池框架的优点

1、能复用已存在并空闲的线程从而减少线程对象的创建从而减少了消亡线程的开销。
2、可有效控制最大并发线程数, 提高系统资源使用率, 同时避免过多资源竞争。
3、框架中已经有定时、定期、单线程、并发数控制等功能。

综上所述使用线程池框架Executor 能更好的管理线程、提供系统资源使用率。

java 中有几种方法可以实现一个线程?

继承Thread 类
实现Runnable 接口
实现Callable 接口,需要实现的是call() 方法

如何停止一个正在运行的线程?

使用共享变量的方式
在这种方式中,之所以引入共享变量,是因为该变量可以被多个执行相同任务的线程用来作为是否中断的信号,通知中断线程的执行。

使用interrupt 方法终止线程
如果一个线程由于等待某些事件的发生而被阻塞, 又该怎样停止该线程呢?这种情况经常会发生,比如当一个线程由于需要等候键盘输入而被阻塞, 或者调用Thread.join()方法,或者Thread.sleep()方法,在网络中调用ServerSocket.accept()方法,或者调用了DatagramSocket.receive()方法时,都有可能导致线程阻塞,使线程处于处于不可运行状态时,即使主程序中将该线程的共享变量设置为true,但该线程此时根本无法检查循环标志, 当然也就无法立即中断。这里我们给出的建议是,不要使用stop()方法,而是使用Thread 提供的interrupt()方法,因为该方法虽然不会中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常, 从而使线程提前结束阻塞状态, 退出堵塞代码。

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

当一个线程进入wait 之后,就必须等其他线程notify/notifyall,使用notifyall,可以唤醒所有处于wait 状态的线程,使其重新进入锁的争夺队列中,而notify 只能唤醒一个。

如果没把握,建议notifyAll,防止notigy 因为信号丢失而造成程序异常。

什么是Daemon 线程?它有什么意义?

所谓后台(daemon)线程,是指在程序运行的时候在后台提供一种通用服务的线程, 并且这个线程并不属于程序中不可或缺的部分。因此, 当所有的非后台线程结束时, 程序也就终止了,同时会杀死进程中的所有后台线程。反过来说,只要有任何非后台线程还在运行,程序就不会终止。必须在线程启动之前调用setDaemon()方法,才能把它设置为后台线程。注意: 后台进程在不执行finally子句的情况下就会终止其run()方法。

比如:JVM 的垃圾回收线程就是Daemon 线程, Finalizer 也是守护线程。

java 如何实现多线程之间的通讯和协作?

中断和共享变量

以上是关于大厂P7 Java程序员高频面试题-4的主要内容,如果未能解决你的问题,请参考以下文章

大厂P7 Java程序员高频面试题-6

大厂P7 Java程序员高频面试题-9

大厂P7 Java程序员高频面试题-2

大厂P7 Java程序员高频面试题-10

大厂P7 Java程序员高频面试题-3

大厂P7 Java程序员高频面试题-12