- java 中 IO 流分为几种?
按照流的流向分,可以分为输入流和输出流; 按照操作单元划分,可以划分为字节流和字符流; 按照流的角色划分为节点流和处理流。
Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联 系, Java I0流的40多个类都是从如下4个抽象类基类中派生出来的。
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。 OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
- BIO,NIO,AIO 有什么区别?
BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通 讯,实现了多路复用。
AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
- 什么是反射机制?
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个 对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java语言的反射机制。
静态编译和动态编译 静态编译:在编译时确定类型,绑定对象
动态编译:运行时确定类型,绑定对象
- 反射机制优缺点
- 优点: 运行期类型的判断,动态加载类,提高代码灵活度。
- 缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。
- 反射机制的应用场景有哪些?
反射是框架的灵魂存在!!
我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序; Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:
1 ) 将程序内所有 XML 或 Properties 配置文件加载入内存中;
2)Java类里面解析xml或 properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息;
3)使用反射机制,根据这 个字符串获得某个类的Class实例;
4)动态配置实例的属性
- Java获取反射的三种方法
- 通过new对象实现反射机制 new对象.getClass();
- 通过路径实现反射机制 Class.forName(路径名)
- 通过类名实现反射机制 类名.class()
-
列出一些你常见的运行时异常
ArithmeticException(算术异常) ClassCastException (类转换异常) IllegalArgumentException (非法参数异常) IndexOutOfBoundsException (下表越界异常) NullPointerException (空指针异常) SecurityException (安全异常) -
Java 实现线程有哪几种方式?
1)继承 Thread 类实现多线程
2)实现 Runnable 接口方式实现多线程
3)使用 ExecutorService、Callable、Future 实现有返回结果的多线程 -
怎么控制同一时间只有 3 个线程运行
Semaphore 怎么使用可以参考我之前的JUC博客
- 线程池启动线程 submit()和 execute()方法有什么不同?
execute 没有返回值,如果不需要知道线程的结果就使用 execute 方法,性能会好很 多。
submit 返回一个 Future 对象,如果想知道线程结果就使用 submit 提交,而且它能 在主线程中通过 Future 的 get 方法捕获线程中的异常。
- CyclicBarrier 和 CountDownLatch 的区别?
两个看上去有点像的类,都在 java.util.concurrent 下,都可以用来表示代码运行到 某个点上,二者的区别在于:
1.CyclicBarrier 的某个线程运行到某个点上之后,该线程即停止运行,直到所有的 线程都到达了这个点,所有线程才重新运行;CountDownLatch 则不是,某线程运 行到某个点上之后,只是给某个数值-1 而已,该线程继续运行
2.CyclicBarrier 只能唤起一个任务,CountDownLatch 可以唤起多个任务 3.CyclicBarrier 可重用, CountDownLatch 不可重用,计数值为 0 该 CountDownLatch就不可再用了
- 什么是活锁、饥饿、无锁、死锁?
死锁、活锁、饥饿是关于多线程是否活跃出现的运行阻塞障碍问题,如果线程出现 了这三种情况,即线程不再活跃,不能再正常地执行下去了。
- 死锁 死锁是多线程中最差的一种情况,多个线程相互占用对方的资源的锁,而又相互等 对方释放锁,此时若无外力干预,这些线程则一直处理阻塞的假死状态,形成死锁。 举个例子,A 同学抢了 B 同学的钢笔,B 同学抢了 A 同学的书,两个人都相互占 用对方的东西,都在让对方先还给自己自己再还,这样一直争执下去等待对方还而 又得不到解决,老师知道此事后就让他们相互还给对方,这样在外力的干预下他们 才解决,当然这只是个例子没有老师他们也能很好解决,计算机不像人如果发现这 种情况没有外力干预还是会一直阻塞下去的。
- 活锁 活锁这个概念大家应该很少有人听说或理解它的概念,而在多线程中这确实存在。 活锁恰恰与死锁相反,死锁是大家都拿不到资源都占用着对方的资源,而活锁是拿 到资源却又相互释放不执行。当多线程中出现了相互谦让,都主动将资源释放给别 的线程使用,这样这个资源在多个线程之间跳动而又得不到执行,这就是活锁。
- 饥饿 我们知道多线程执行中有线程优先级这个东西,优先级高的线程能够插队并优先执 行,这样如果优先级高的线程一直抢占优先级低线程的资源,导致低优先级线程无 法得到执行,这就是饥饿。当然还有一种饥饿的情况,一个线程一直占着一个资源 不放而导致其他线程得不到执行,与死锁不同的是饥饿在以后一段时间内还是能够 得到执行的,如那个占用资源的线程结束了并释放了资源。
- 无锁 无锁,即没有对资源进行锁定,即所有的线程都能访问并修改同一个资源,但同时 只有一个线程能修改成功。无锁典型的特点就是一个修改操作在一个循环内进行, 线程会不断的尝试修改共享资源,如果没有冲突就修改成功并退出否则就会继续下 一次循环尝试。所以,如果有多个线程修改同一个值必定会有一个线程能修改成功, 而其他修改失败的线程会不断重试直到修改成功。之前的文章我介绍过 JDK 的 CAS 原理及应用即是无锁的实现。 可以看出,无锁是一种非常良好的设计,它不会出现线程出现的跳跃性问题,锁使 用不当肯定会出现系统性能问题,虽然无锁无法全面代替有锁,但无锁在某些场合 下是非常高效的。
- 什么是原子性、可见性、有序性?
原子性、可见性、有序性是多线程编程中最重要的几个知识点,由于多线程情况复 杂,如何让每个线程能看到正确的结果,这是非常重要的。
- 原子性 原子性是指一个线程的操作是不能被其他线程打断,同一时间只有一个线程对一个 变量进行操作。在多线程情况下,每个线程的执行结果不受其他线程的干扰,比如 说多个线程同时对同一个共享成员变量 n++100 次,如果 n 初始值为 0,n 最后的 值应该是 100,所以说它们是互不干扰的,这就是传说的中的原子性。但 n++并不 是原子性的操作,要使用 AtomicInteger 保证原子性。
- 可见性 可见性是指某个线程修改了某一个共享变量的值,而其他线程是否可以看见该共享 变量修改后的值。在单线程中肯定不会有这种问题,单线程读到的肯定都是最新的
值,而在多线程编程中就不一定了。每个线程都有自己的工作内存,线程先把共享 变量的值从主内存读到工作内存,形成一个副本,当计算完后再把副本的值刷回主 内存,从读取到最后刷回主内存这是一个过程,当还没刷回主内存的时候这时候对 其他线程是不可见的,所以其他线程从主内存读到的值是修改之前的旧值。像 CPU 的缓存优化、硬件优化、指令重排及对 JVM 编译器的优化,都会出现可见性 的问题。 - 有序性 我们都知道程序是按代码顺序执行的,对于单线程来说确实是如此,但在多线程情 况下就不是如此了。为了优化程序执行和提高 CPU 的处理性能,JVM 和操作系统 都会对指令进行重排,也就说前面的代码并不一定都会在后面的代码前面执行,即 后面的代码可能会插到前面的代码之前执行,只要不影响当前线程的执行结果。所 以,指令重排只会保证当前线程执行结果一致,但指令重排后势必会影响多线程的 执行结果。虽然重排序优化了性能,但也是会遵守一些规则的,并不能随便乱排序, 只是重排序会影响多线程执行的结果。
- 什么是守护线程?有什么用?
什么是守护线程?与守护线程相对应的就是用户线程,守护线程就是守护用户线 程,当用户线程全部执行完结束之后,守护线程才会跟着结束。也就是守护线程必 须伴随着用户线程,如果一个应用内只存在一个守护线程,没有用户线程,守护线 程自然会退出。
- 、一个线程运行时发生异常会怎样?
如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler 是用 于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常将造 成线程中断的时 候 JVM 会 使 用 Thread.getUncaughtExceptionHandler() 来 查 询 线 程的UncaughtExceptionHandler并将线程和异常作为参数传递给handler的 uncaughtException()方法进行处理。
- 线程 yield()方法有什么用?
Yield 方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。 它是一个静态方法而且只保证当前线程放弃 CPU 占用而不能保证使其它线程一定 能占用 CPU,执行yield()的线程有可能在进入到暂停状态后马上又被执行。
- 什么是重入锁?
所谓重入锁,指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再 次获取本对象上的锁,而其他的线程是不可以的。
- Synchronized 有哪几种用法?
锁类、锁方法、锁代码块。
- Fork/Join 框架是干什么的?
大任务自动分散小任务,并发执行,合并小任务结果。
- 线程数过多会造成什么异常?
线程过多会造成栈溢出,也有可能会造成堆异常。