Skip to content

1、Java 线程数过多会造成什么异常?

1、 线程的生命周期开销非常高

2、 消耗过多的 CPU

资源如果可运行的线程数量多于可用处理器的数量,那么有线程将会被闲置。大量空闲的线程会占用许多内存,给垃圾回收器带来压力,而且大量的线程在竞争 CPU资源时还将产生其他性能的开销。

降低稳定性JVM

在可创建线程的数量上存在一个限制,这个限制值将随着平台的不同而不同,并且承受着多个因素制约,包括 JVM 的启动参数、Thread 构造函数中请求栈的大小,以及底层操作系统对线程的限制等。如果破坏了这些限制,那么可能抛出OutOfMemoryError 异常。

2、同步方法和同步块,哪个是更好的选择?

同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。

同步块更要符合开放调用的原则,只在需要锁住的代码块锁住相应的对象,这样从侧面来说也可以避免死锁。

3、说说类加载的过程

加载 验证 准备(为一些类变量分配内存,并将其初始化为默认值) 解析(将符号引用替换为直接引用。类和接口、类方法、接口方法、字段等解析) 初始化

4、程序计数器是什么?

程序计数器是一块较小的内存空间,可以看作当前线程所执行字节码的行号指示器。字节码解释器工作时通过改变计数器的值选取下一条执行指令。分支、循环、跳转、线程恢复等功能都需要依赖计数器完成。是唯一在虚拟机规范中没有规定内存溢出情况的区域。

如果线程正在执行 Java 方法,计数器记录正在执行的虚拟机字节码指令地址。如果是本地方法,计数器值为 Undefined。

5、ReadWriteLock是什么

首先明确一下,不是说ReentrantLock不好,只是ReentrantLock某些时候有局限。如果使用ReentrantLock,可能本身是为了防止线程A在写数据、线程B在读数据造成的数据不一致,但这样,如果线程C在读数据、线程D也在读数据,读数据是不会改变数据的,没有必要加锁,但是还是加锁了,降低了程序的性能。

因为这个,才诞生了读写锁ReadWriteLock。ReadWriteLock是一个读写锁接口,ReentrantReadWriteLock是ReadWriteLock接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。

6、synchronized 和 Lock 有什么区别?

1、 首先synchronized是Java内置关键字,在JVM层面,Lock是个Java类;

2、 synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。

3、 synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。

4、 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。

7、为什么线程通信的方法 wait(), notify()和 notifyAll()被定义在 Object 类里?

因为Java所有类的都继承了Object,Java想让任何对象都可以作为锁,并且 wait(),notify()等方法用于等待对象的锁或者唤醒线程,在 Java 的线程中并没有可供任何对象使用的锁,所以任意对象调用方法一定定义在Object类中。

有的人会说,既然是线程放弃对象锁,那也可以把wait()定义在Thread类里面啊,新定义的线程继承于Thread类,也不需要重新定义wait()方法的实现。然而,这样做有一个非常大的问题,一个线程完全可以持有很多锁,你一个线程放弃锁的时候,到底要放弃哪个锁?当然了,这种设计并不是不能实现,只是管理起来更加复杂。

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

可以通过中断 和 共享变量的方式实现线程间的通讯和协作

比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。这种互相通信的过程就是线程间的协作。

Java中线程通信协作的最常见方式:

1、 syncrhoized加锁的线程的Object类的wait()/notify()/notifyAll()

2、 ReentrantLock类加锁的线程的Condition类的await()/signal()/signalAll()

线程间直接的数据交换:

通过管道进行线程间通信:字节流、字符流

9、怎么判断并发队列是阻塞队列还是非阻塞队列

在并发队列上JDK提供了Queue接口,一个是以Queue接口下的BlockingQueue接口为代表的阻塞队列,另一个是高性能(无堵塞)队列。

10、volatile 能使得一个非原子操作变成原子操作吗?

1、 关键字volatile的主要作用是使变量在多个线程间可见,但无法保证原子性,对于多个线程访问同一个实例变量需要加锁进行同步。

2、 虽然volatile只能保证可见性不能保证原子性,但用volatile修饰long和double可以保证其操作原子性。

所以从Oracle Java Spec里面可以看到:

1、 对于64位的long和double,如果没有被volatile修饰,那么对其操作可以不是原子的。在操作的时候,可以分成两步,每次对32位操作。

2、 如果使用volatile修饰long和double,那么其读写都是原子操作

3、 对于64位的引用地址的读写,都是原子操作

4、 在实现JVM时,可以自由选择是否把读写long和double作为原子操作

5、 推荐JVM实现为原子操作

11、SWAP会影响性能么?

12、你是如何调用 wait() 方法的?使用 if 块还是循环?为什么?

13、如何合理分配线程池大小?

14、在新生代-复制算法

15、怎么打出线程栈信息?

16、代码示例:

17、在java中守护线程和本地线程区别?

18、你对线程优先级的理解是什么?

19、被引用的对象就一定能存活吗?

20、类加载的过程是什么?

21、什么是阻塞式方法?

22、线程之间如何通信及线程之间如何同步

23、非堵塞队列:

24、四种构建线程池的区别及特点?

25、说说 JVM 如何执行 class 中的字节码。

26、ThreadPoolExecutor饱和策略有哪些?

27、守护线程和用户线程有什么区别呢?

28、GC垃圾回收算法与垃圾收集器的关系?

29、Java 中垃圾收集的方法有哪些

30、分代收集算法

31、GC Roots 有哪些?

32、什么是Callable和Future?

用心去做高质量的内容网站,欢迎 star ⭐ 让更多人发现