Java技术杂谈

 一、多线程

1、线程池的原理,为什么要创建线程池?创建线程池的方式;

原理:

JAVA线程池原理详解一

JAVA线程池原理详解二

创建线程池的几种方式:

ThreadPoolExecutor、ThreadScheduledExecutor、ForkJoinPool

2、线程的生命周期,什么时候会出现僵死进程;

僵死进程是指子进程退出时,父进程并未对其发出的SIGCHLD信号进行适当处理,导致子进程停留在僵死状态等待其父进程为其收尸,这个状态下的子进程就是僵死进程。

3、说说线程安全问题,什么是线程安全,如何实现线程安全;

线程安全 - 如果线程执行过程中不会产生共享资源的冲突,则线程安全。

线程不安全 - 如果有多个线程同时在操作主内存中的变量,则线程不安全

实现线程安全的三种方式

1)互斥同步

临界区:syncronized、ReentrantLock

信号量 semaphore

互斥量 mutex

2)非阻塞同步

CAS(Compare And Swap)

3)无同步方案

可重入代码

使用Threadlocal 类来包装共享变量,做到每个线程有自己的copy

线程本地存储

Java多线程安全机制-CSDN博客

4、创建线程池有哪几个核心参数? 如何合理配置线程池的大小?

1)核心参数

public ThreadPoolExecutor(int corePoolSize,// 核心线程数量大小

                                             int maximumPoolSize, // 线程池最大容纳线程数

                                                long keepAliveTime, // 线程空闲后的存活时长

                                                TimeUnit unit,

                                                /缓存异步任务的队列 //用来构造线程池里的worker线程

                                                BlockingQueue<Runnable> workQueue,

                                                ThreadFactory threadFactory,

                                                //线程池任务满载后采取的任务拒绝策略

                                                RejectedExecutionHandler handler)

2) 核心说明

1、当线程池中线程数量小于 corePoolSize 则创建线程,并处理请求。

2、当线程池中线程数量大于等于 corePoolSize 时,则把请求放入 workQueue 中,随着线程池中的核心线程们不断执行任务,只要线程池中有空闲的核心线程,线程池就从 workQueue 中取任务并处理。

3 、当 workQueue 已存满,放不下新任务时则新建非核心线程入池,并处理请求直到线程数目达到 maximumPoolSize(最大线程数量设置值)。

4、如果线程池中线程数大于 maximumPoolSize 则使用 RejectedExecutionHandler 来进行任务拒绝处理。

参考:http://gudong.name/2017/05/03/thread-pool-intro.html

3)线程池大小分配

线程池究竟设置多大要看你的线程池执行的什么任务了,CPU密集型、IO密集型、混合型,任务类型不同,设置的方式也不一样。

任务一般分为:CPU密集型、IO密集型、混合型,对于不同类型的任务需要分配不同大小的线程池。

3.1)CPU密集型

尽量使用较小的线程池,一般Cpu核心数+1

3.2)IO密集型

方法一:可以使用较大的线程池,一般CPU核心数 * 2

方法二:(线程等待时间与线程CPU时间之比+1)* CPU数目

3.3)混合型

可以将任务分为CPU密集型和IO密集型,然后分别使用不同的线程池去处理,按情况而定

参考:java线程池如何合理的设置大小 - 提拉没有米苏 - 博客园

5、volatile、ThreadLocal的使用场景和原理;

volatile原理

volatile变量进行写操作时,JVM 会向处理器发送一条 Lock 前缀的指令,将这个变量所在缓存行的数据写会到系统内存。

Lock 前缀指令实际上相当于一个内存屏障(也成内存栅栏),它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成。

volatile的适用场景

1)状态标志,如:初始化或请求停机

2)一次性安全发布,如:单列模式

3)独立观察,如:定期更新某个值

4)“volatile bean” 模式

5) 开销较低的“读-写锁”策略,如:计数器

参考:volatile的使用场景_volatile使用场景-CSDN博客

参考:IBM Developer

ThreadLocal原理

        ThreadLocal是用来维护本线程的变量的,并不能解决共享变量的并发问题。

        ThreadLocal是各线程将值存入该线程的map中,以ThreadLocal自身作为key,需要用时

获得的是该线程之前存入的值。如果存入的是共享变量,那取出的也是共享变量,并发问题

还是存在的。

参考:对ThreadLocal实现原理的一点思考 - 简书

参考:Java多线程编程-(8)-多图深入分析ThreadLocal原理_csdn+threadlocal+徐刘根-CSDN博客

ThreadLocal的适用场景

场景:数据库连接、Session管理、

参考:https://www.jianshu.com/p/cadd53f063b9

6、ThreadLocal什么时候会出现OOM的情况?为什么?

ThreadLocal变量是维护在Thread内部的,这样的话只要我们的线程不退出,对象的引用

就会一直存在。当线程退出时,Thread类会进行一些清理工作,其中就包含

ThreadLocalMap,Thread调用exit方法如下: 

ThreadLocal在没有线程池使用的情况下,正常情况下不会存在内存泄露,但是如果使用了

线程池的话,就依赖于线程池的实现,如果线程池不销毁线程的话,那么就会存在内存泄

露。

参考:Java多线程编程-(8)-多图深入分析ThreadLocal原理_csdn+threadlocal+徐刘根-CSDN博客

7、synchronized、volatile区别

1) volatile主要应用在多个线程对实例变量更改的场合,刷新主内存共享变量的值从而使得

各个线程可以获得最新的值,线程读取变量的值需要从主存中读取;synchronized则是锁

定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。另外,synchronized

会创建一个内存屏障,内存屏障指令保证了所有CPU操作结果都会直接刷到主存中(即释放

锁前),从而保证了操作的内存可见性,同时也使得先获得这个锁的线程的所有操作

2) volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。

volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞,比如多个线程争抢

synchronized锁对象时,会出现阻塞。

3) volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变

量的修改可见性和原子性,因为线程获得锁才能进入临界区,从而保证临界区中的所有语句

全部得到执行。

4) volatile标记的变量不会被编译器优化,可以禁止进行指令重排;synchronized标记的变量可以被编译器优化。

参考:synchronized和volatile区别-CSDN博客 

8、synchronized锁粒度、模拟死锁场景;

synchronized:具有原子性,有序性和可见性

参考:三大性质总结:原子性,有序性,可见性 - 简书

粒度:对象锁、类锁

死锁场景,参考:基于synchronized的对象锁,类锁以及死锁模拟_基于synchronized的对象锁,类锁以及死锁模拟-CSDN博客

二、Jvm相关

1、JVM内存模型,GC机制和原理;

内存模型

Jdk1.6及之前:有永久代, 常量池在方法区

Jdk1.7:有永久代,但已经逐步“去永久代”,常量池在堆

Jdk1.8及之后: 无永久代,常量池在元空间

2、GC分哪两种,Minor GC 和Full GC有什么区别?什么时候会触发Full GC?分别采用什么算法?

对象从新生代区域消失的过程,我们称之为 "minor GC"

对象从老年代区域消失的过程,我们称之为 "major GC"

Minor GC

清理整个YouGen的过程,eden的清理,S0\S1的清理都会由于MinorGC Allocation

Failure(YoungGen区内存不足),而触发minorGC

Major GC

OldGen区内存不足,触发Major GC

Full GC

Full GC 是清理整个堆空间—包括年轻代和永久代

Full GC 触发的场景

1)System.gc

2)promotion failed (年代晋升失败,比如eden区的存活对象晋升到S区放不下,又尝试直接晋升到Old区又放不下,那么Promotion Failed,会触发FullGC)

3)CMS的Concurrent-Mode-Failure

由于CMS回收过程中主要分为四步: 1.CMS initial mark 2.CMS Concurrent mark 3.CMS remark 4.CMS Concurrent sweep。在2中gc线程与用户线程同时执行,那么用户线程依旧可能同时产生垃圾, 如果这个垃圾较多无法放入预留的空间就会产生CMS-Mode-Failure, 切换为SerialOld单线程做mark-sweep-compact。

4)新生代晋升的平均大小大于老年代的剩余空间 (为了避免新生代晋升到老年代失败)当使用G1,CMS 时,FullGC发生的时候 是 Serial+SerialOld。

当使用ParalOld时,FullGC发生的时候是 ParallNew +ParallOld.

3、JVM里的有几种classloader,为什么会有多种?

启动类加载器:负责加载JRE的核心类库,如jre目标下的rt.jar,charsets.jar等

扩展类加载器:负责加载JRE扩展目录extJAR类包

系统类加载器:负责加载ClassPath路径下的类包

用户自定义加载器:负责加载用户自定义路径下的类包

为什么会有多种:

1)分工,各自负责各自的区块

2)为了实现委托模型

4、什么是双亲委派机制?介绍一些运作过程,双亲委派模型的好处;

如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父

类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,

请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,

倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模

式,即每个儿子都不愿意干活,每次有活就丢给父亲去干,直到父亲说这件事我也干不了

时,儿子自己想办法去完成,这不就是传说中的双亲委派模式。

好处

沙箱安全机制:自己写的String.class类不会被加载,这样便可以防止核心API库被随意篡

避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次

5、什么情况下我们需要破坏双亲委派模型;

6、常见的JVM调优方法有哪些?可以具体到调整哪个参数,调成什么值?

调优工具

console,jProfile,VisualVM

Dump线程详细信息:查看线程内部运行情况

死锁检查

查看堆内类、对象信息查看:数量、类型等

线程监控

线程信息监控:系统线程数量。

线程状态监控:各个线程都处在什么样的状态下

热点分析

CPU热点:检查系统哪些方法占用的大量CPU时间

内存热点:检查哪些对象在系统中数量最大(一定时间内存活对象和销毁对象一起统计)

内存泄漏检查

7、JVM虚拟机内存划分、类加载器、垃圾收集算法、垃圾收集器、

class文件结构是如何解析的;

垃圾收集算法:标记-清除算法、复制算法、标记-整理算法、分代收集算法

垃圾收集器: Serial收集器、ParNew收集器、Parallel Scavenge收集器、Serial Old收集器、Parallel Old收集器、CMS收集器、G1收集器、Z垃圾收集器

class文件结构是如何解析的

解悉过程:https://blog.csdn.net/sinat_38259539/article/details/7824845

三、Java扩展篇

1、红黑树的实现原理和应用场景;

红黑树(一棵自平衡的排序二叉树)五大特性:

1)每个结点要么是红的,要么是黑的。 

2)根结点是黑的。 

3)每个叶结点,即空结点是黑的。 

4)如果一个结点是红的,那么它的俩个儿子都是黑的。 

5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。

场景

1)广泛用于C++的STL中,map和set都是用红黑树实现的.

2)著名的linux进程调度Completely Fair Scheduler,用红黑树管理进程控制块,进程的虚拟

内存区域都存储在一颗红黑树上,每个虚拟地址区域都对应红黑树的一个节点,左指针指向相

邻的地址虚拟存储区域,右指针指向相邻的高地址虚拟地址空间.

3)IO多路复用epoll的实现采用红黑树组织管理sockfd,以支持快速的增删改查.

4)ngnix中,用红黑树管理timer,因为红黑树是有序的,可以很快的得到距离当前最小的定时

器.

5)java中的TreeSet,TreeMap

2、NIO是什么?适用于何种场景?

 (New IO)为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。

特性:I/O多路复用 + 非阻塞式I/O

NIO适用场景

服务器需要支持超大量的长时间连接。比如10000个连接以上,并且每个客户端并不会频繁

地发送太多数据。例如总公司的一个中心服务器需要收集全国便利店各个收银机的交易信

息,只需要少量线程按需处理维护的大量长期连接。

Jetty、Mina、Netty、ZooKeeper等都是基于NIO方式实现。

【NIO技术概览】

3、Java9比Java8改进了什么;

1)引入了模块系统,采用模块化系统的应用程序只需要这些应用程序所需的那部分JDK模

块,而非是整个JDK框架了,减少了内存的开销。

2)引入了一个新的package:java.net.http,里面提供了对Http访问很好的支持,不仅支持

Http1.1而且还支持HTTP2。

3)引入了jshell这个交互性工具,让Java也可以像脚本语言一样来运行,可以从控制台启动 jshell ,在 jshell 中直接输入表达式并查看其执行结果。

4)增加了List.of()、Set.of()、Map.of()和Map.ofEntries()等工厂方法来创建不可变集合

5)HTML5风格的Java帮助文档

6)多版本兼容 JAR 功能,能让你创建仅在特定版本的 Java 环境中运行库程序时选择使用的 class 版本。

7)统一 JVM 日志

可以使用新的命令行选项-Xlog 来控制JVM 上 所有组件的日志记录。该日志记录系统可以设置输出的日志消息的标签、级别、修饰符和输出目标等。

8)垃圾收集机制

移除了在 Java 8 中 被废弃的垃圾回收器配置组合,同时把G1设为默认的垃圾回收器实现.因为相对于Parallel来说,G1会在应用线程上做更多的事情,而Parallel几乎没有在应用线程上做任何事情,它基本上完全依赖GC线程完成所有的内存管理。这意味着切换到G1将会为应用线程带来额外的工作,从而直接影响到应用的性能

9)I/O 流新特性

java.io.InputStream 中增加了新的方法来读取和复制 InputStream 中包含的数据。

readAllBytes:读取 InputStream 中的所有剩余字节。

readNBytes: 从 InputStream 中读取指定数量的字节到数组中。

transferTo:读取 InputStream 中的全部字节并写入到指定的 OutputStream中;

4、HashMap内部的数据结构是什么?底层是怎么实现的?

HashMap内部结构

jdk8以前:数组+链表

jdk8以后:数组+链表 (当链表长度到8时,转化为红黑树

在并发的情况,发生扩容时,可能会产生循环链表,在执行get的时候,会触发死循环,引 起CPU的100%问题,所以一定要避免在并发环境下使用HashMap

5、延伸考察ConcurrentHashMap与HashMap、HashTable等,考察对技术细节的深入了解程度;

HashMap、HashTable、ConcurrentHashMap的原理与区别

老生常谈,HashMap的死循环 ConcurrentHashMap在jdk1.8中的改进

谈谈ConcurrentHashMap1.7和1.8的不同实现                                  深入分析ConcurrentHashMap1.8的扩容实现深入浅出ConcurrentHashMap1.8 ConcurrentHashMap的红黑树实现分析

6、说说反射的用途及实现,反射是不是很慢,我们在项目中是否要避免使用反射;

一、用途

反射被广泛地用于那些需要在运行时检测或修改程序行为的程序中。

二、实现方式

Foo foo = new Foo();

第一种:通过Object类的getClass方法 Class cla = foo.getClass();

第二种:通过对象实例方法获取对象 Class cla = foo.class;

第三种:通过Class.forName方式

Class cla = Class.forName("xx.xx.Foo");

三、缺点 

影响性能

反射包括了一些动态类型,所以 JVM 无法对这些代码进行优化。因此,反射操作的效 率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程 序中使用反射。

 安全限制

使用反射技术要求程序必须在一个没有安全限制的环境中运行。

 内部暴露

由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方 

法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植 性。 反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

7、说说自定义注解的场景及实现;

利用自定义注解,结合SpringAOP可以完成权限控制、日志记录、统一异常处理、数字签

名、数据加解密等功能。

实现场景(API接口数据加解密

  1. 自定义一个注解,在需要加解密的方法上添加该注解
  2. 配置SringAOP环绕通知
  3. 截获方法入参并进行解密
  4. 截获方法返回值并进行加密

8、List 和 Map 区别

一、概述
List是存储单列数据的集合,Map是存储键和值这样的双列数据的集合, List中存储的数据是有顺序,并且允许重复,值允许有多个null;
Map中存储的数据是没有顺序的,键不能重复,值是可以有重复的,key最多有一个null。

二、明细 List
1)可以允许重复的对象。
2)可以插入多个null元素。
3)是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。
4)常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用 索引的随意访问,而 LinkedList 则对于经常需要从 List 中添加或删除元素的场合更为合适。

Map
1)Map不是collection的子接口或者实现类。Map是一个接口。
2)Map 的 每个 Entry 都持有两个对象,也就是一个键一个值,Map 可能会持有相同的值 对象但键对象必须是唯一的。
3)TreeMap 也通过 Comparator 或者 Comparable 维护了一个排序顺序。 4)Map 里你可以拥有随意个 null 值但最多只能有一个 null 键。
5)Map 接口最流行的几个实现类是 HashMap、LinkedHashMap、Hashtable 和  TreeMap。(HashMap、TreeMap最常用)

Set(问题扩展) 1)不允许重复对象
2)无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或 Comparable 维护了一个排序顺序。
3)只允许一个 null 元素
4)Set 接口最流行的几个实现类是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是 基于 HashMap 实现的 HashSet;TreeSet 还实现了 SortedSet 接口,因此 TreeSet 是一个根据其 compare() 和 compareTo() 的定义进行排序的有序容器。

三、场景(问题扩展)
1)如果你经常会使用索引来对容器中的元素进行访问,那么 List 是你的正确的选择。如果 你已经知道索引了的话,那么 List 的实现类比如 ArrayList 可以提供更快速的访问,如果经常添加删除元素的,那么肯定要选择LinkedList。
2)如果你想容器中的元素能够按照它们插入的次序进行有序存储,那么还是 List,因为  List 是一个有序容器,它按照插入顺序进行存储。
3)如果你想保证插入元素的唯一性,也就是你不想有重复值的出现,那么可以选择一个  Set 的实现类,比如 HashSet、LinkedHashSet 或者 TreeSet。所有 Set 的实现类都遵循 了统一约束比如唯一性,而且还提供了额外的特性比如 TreeSet 还是一个 SortedSet,所有存储于 TreeSet 中的元素可以使用 Java 里的 Comparator 或者 Comparable 进行排序。LinkedHashSet 也按照元素的插入顺序对它们进行存储。
4)如果你以键和值的形式进行数据存储那么 Map 是你正确的选择。你可以根据你的后续需要从 Hashtable、HashMap、TreeMap 中进行选择。
参考:List、Set、Map的区别

9、Arraylist 与 LinkedList 区别,ArrayList 与 Vector 区别;

1数据结构

Vector、ArrayList内部使用数组,而LinkedList内部使用双向链表,由数组和链表的特性知:

LinkedList适合指定位置插入、删除操作,不适合查找; ArrayList、Vector适合查找,不适合指定位置的插入删除操作。

但是ArrayList越靠近尾部的元素进行增删时,其实效率比LinkedList要高

2)线程安全

Vector线程安全,ArrayList、LinkedList线程不安全。

3空间

ArrayList在元素填满容器时会自动扩充容器大小的50%,而Vector则是100%,因此 ArrayList更节省空间。

参考:ArrayList和LinkedList内部实现、区别、使用场景

四、Spring相关

1、Spring AOP的实现原理和场景;

AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处

理一些具有横切性质的系统级服务。

一、场景
 事务管理、安全检查、权限控制、数据校验、缓存、对象池管理等
 
二、实现技术
 AOP(这里的AOP指的是面向切面编程思想&#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值