印象笔记java

154 阅读44分钟

JVM,多线程,锁,容器,Java基础,其他,待答。

JVM

  • 第27题:OOM内存泄漏,什么情况下会出现,如何排查。 内存溢出:内存不够,内存泄漏/OOM:对象内存不回收。 堆溢出:判断是内存泄漏,还是内存溢出。 报错为:java heap space。 内存泄漏查看GCRoot引用链,内存溢出调整参数和对象生命周期是否过长。 方法区溢出:常量池溢出或者,不停的有类动态创建并加载。 报错为:PemGen space。 栈溢出:虚拟机栈溢出与本地方法栈溢出。 方法不停的递归调用,使得其他方法不停的入栈。 报错为:stack over flow。 本地直接内存溢出:程序中使用了NIO技术。

  • 第15题.Java会出现内存溢出吗?什么情况下会出现? 内存溢出-内存不够,内存泄漏OOM-对象内存不回收。 堆溢出:判断是内存泄漏OOM,还是内存溢出。 内存泄漏查看GCRoot引用链,内存溢出调整参数和对象生命周期是否过长。 方法区溢出:常量池溢出或者,不停的有类动态创建并加载。 栈溢出:虚拟机栈溢出与本地方法栈溢出。 方法不停的递归和调用,使得其他方法不停的入栈。报错为stack over flow。 本地直接内存溢出:程序中使用了NIO技术。

  • 第16题.(类加载)双亲委派模型,为什么这样做? 双亲委派:类加载任务委派给父类加载器,依次递归,父类加载器加载失败时自己去加载。 Java类随着它的类加载器一起具备了带有优先级的层次关系,当父类已经加载该类时加载器没必要再加载一次。

  • 第17题. 对象什么情况下进入老年代? 默认情况下,YGC 15次进入老年代。

  • 第10题:目前的JDK使用的是什么垃圾回收算法,为什么要用这个算法? 分代收集算法,年轻代使用复制算法,年老代使用标记清除或者标记整理(减少内存碎片)。 不同年代对象的存活时间不一样。

第4题:为什么要用老年代和新生代? 不同年代的对象存活时间不一样,一般年轻代的对象存活时间短,年老代的对象存活时间长,不同的年代使用不同的垃圾回收算法。

  • 内存泄漏OOM与内存溢出的区别。 内存溢出:没有多余的内存分配给对象。 内存泄漏:对象没有被及时垃圾回收,多次内存泄漏导致内存溢出。

  • java内存分配策略,多个线程同时请求内存,如何分配? 共享变量分配在堆,线程的内存分配在线程的工作内存,工作内存间相互独立,共享变量从堆复制到工作内存。

  • GC,G1和ZGC的区别。 ZGC收集器:可伸缩的低延时的收集器。 ZGC的目标:支持TB级内存,停顿时间低于10ms,吞吐量影响小于15%

  • 变量从产生到结束的所有过程,讲下字符串常量的过程。 类变量/static:能字节码加载进JVM到JVM停止,存储在方法区。 类变量/无static:类实例化到对象被JVM回收,存储在堆内存。 局部变量:代码执行到变量到,代码/方法结束,存储在当前方法栈帧。 字符串常量的过程:字节码加载到JVM停止。

第23题:类加载过程,类加载机制?双亲委派,双亲委派的优势。 类加载过程?加载,验证,准备,解析,初始化。 类加载机制?有3中: 全盘负责:当一个类加载器负责加载某个类时,该类所依赖和引用其他类也将由该类加载器负责载入,除非显示使用另外累加载器来载入。 双亲委派:首先将类加载任务委派给父类加载器,依次递归,父类加载器无法完成加载任务时才自己去加载。 缓存机制:所有加载过的类都被缓存,当需要某个类时从缓存区中寻找该类,缓存里不存在时才会加载该类并将加载的类存入缓冲区。 双亲委派机制:类加载器收到类加载请求后,不自己去加载,而是把类加载任务委托给父类,如果父类还存在其他父类则进一步向上委托直到达到顶层的启动类加载器。 双亲委派的优势:类随着类加载器具备了优先级的层次关系。父类已经加载该类时没必要再加载一次。

第24题:代理模式,动态代理,静态代理,静态代理的优缺点等。 什么是代理模式?对目标对象的间接访问,即通过代理对象访问目标对象。 什么是静态代理?代理类通过实现与目标对象相同的接口,并在类中维护一个代理对象。 静态代理的优缺点:不修改目标对象的前提下,对目标对象进行功能扩展和拦截,但是,导致代理类非常多不易维护。 什么是动态代理?动态的在内存中构建代理对象。 动态代理的优缺点:代理对象不用实现接口,接口增加方法时代理对象与目标对象都不用维护只需要事件处理器添加方法的判断即可。但是目标对象一定要实现接口否则无法使用JDK动态代理。

第12题:Java的内存模型,垃圾回收算法。 堆:分为年轻代和老年代,存储对象实例与部分数组。 方法区:虚拟机加载的类信息,常量,静态变量,线程共享的内存区域。 虚拟机栈:Java方法执行的内存模型,每个方法执行时都会创建一个栈帧。 本地方法栈:Native方法服务。 程序计数器:当前线程执行的字节码的行号。 直接内存:本地内存,NIO中使用。

标记清除-年老代: 从GC Root扫描,标记存活对象,清除没有标记的对象。 标记清除算法的缺点:空间碎片,可能提前触发Full GC。 复制算法-年轻代: 从GC Root扫描,标记存活对象,复制到一块新的内存。 复制算法的缺点:需要一块空的内存空间。 标记整理-老年代: 从GC Root扫描,标记存活对象,清除没有标记的对象,存活对象左移到一起。 缺点:扫描整个空间两次。 分代收集: 年轻代用复制算法,老年代用标记清除或者标记整理算法。

题:对象的引用,分为几类? 强引用,软引用,弱引用,虚引用。 1-强引用:最普遍的引用,当内存空间不足宁愿抛出OutOfMemoryError错误,也不会靠随意回收具有强引用的对象来解决内存不足的问题。 2-软引用:内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。 3-弱引用:弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。 4-虚引用:虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。

题:JVM哪三种垃圾回收器? 年轻代垃圾回收器:Serial,ParallerScanvege-高吞吐,ParNew 年老代垃圾回收器:CMS-响应优先 分代垃圾回收器:G1

第2题:G1收集器对CMS的改进?(内存碎片,停顿时间,多核CPU) 1)年老代使用标记整理算法,减少内存碎片 2)G1收集器的停顿时间可控,可以设置,防止服务器雪崩。 3)G1收集器能更好的利用多核CPU。

  • 第1题:常用的JVM参数配置? -Xms设置堆的最小空间大小。 -Xmx设置堆的最大空间大小。 -Xmn:设置年轻代大小 -XX:NewSize设置新生代最小空间大小。 -XX:MaxNewSize设置新生代最大空间大小。 -XX:PermSize设置永久代最小空间大小。 -XX:MaxPermSize设置永久代最大空间大小。 -Xss设置每个线程的堆栈大小 -XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。 -XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。 典型JVM参数配置参考: java-Xmx3550m-Xms3550m-Xmn2g-Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC-XX:+UseParNewGC -Xmx3550m:设置JVM最大可用内存为3550M。 -Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。 -Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小+年老代大小+持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,官方推荐配置为整个堆的3/8。 -Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大 小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000 左右。

  • 第1题:如何查看java内存使用情况?jconsole,jmap,jstack等 jdk小工具或命令行: jstack:查看线程堆栈。 jstat:查看堆的详细使用情况。 jps:查询正在运行的java虚拟机进程。 jmap:生产虚拟机内存快照。 jhat:分析jmap产生的内存快照文件。 jinfo:查看虚拟机配置参数。

  • 第3题:Java GC机制?GC Roots有哪些? GC 机制: 1.新对象优先分配在Eden区。 2.Eden区满了,其中存活对象复制到Survivor-0,如果Survivor-0放不下则存活对象全部进入老年代,并且全部Eden区回收掉。 3.新对象继续放入Eden区,Eden区满了,复制Eden区和Survivor-0存活下的对象到Survivor-1,存活的对象Survivor-1放不下则全部进入老年代。 4.反复进行默认15次回收后,仍存活对象进入老年代。 5.老年代满了,发生一次Full GC。 GC Root有哪些? 1.虚拟机栈中引用的对象。 2.方法区中类静态属性引用的对象。 3.方法区中常量引用的对象。 4.本地方法栈中JNI本地方法引用的对象。

  • 第二题:垃圾回收算法以及垃圾回收器 。 垃圾收集器: Serial:新生代,单线程,复制算法。 ParNew:新生代,多线程,复制算法。 Paraller Scanvege:新生代,复制算法。 CMS收集器 - 年老代:标记清除算法,缺点:内存碎片。 CMS收集器的步骤:初始标记,并发标记,重新标记,并发清除。 其中,初始标记,重新标记阶段是串型运行,需要Stop the world。 CMS缺点:产生内存碎片,导致Full GC。 G1收集器:新生代和年老代,分代收集算法。 G1收集器,不需要Eden,年轻代,年老代的空间连续。 G1收集器的年老代使用标记整理算法,不产生内存碎片。

  • 第三题:CMS的回收步骤 。 CMS收集器的步骤:初始标记,并发标记,重新标记,并发清除。 其中,初始标记,重新标记阶段是串型运行,需要Stop the world。

  • 第四题:G1和CMS的区别 。 区别1:CMS分为年轻代,年老代,年轻代又分为俩survivor区和eden区。 在G1收集器中,堆平均分成几个区域region,虽然保留年轻代和年老代的概念,但是垃圾收集是整个区域为单位收集的。 区别2:G1在Young GC中使用,CMS在O区使用。

  • 第5题:CMS哪个阶段是并发的哪个阶段是串行的? 初始标记,重新标记阶段是串行运行,需要Stop the world。

  • 第6题:G1内部是如何分区的(region) 。 堆内存分为很多固定大小的区域region,每个区域region是连续的虚拟内存。 一个区域region的大小通过参数:XX:G1HeapRegionSize 指定,大小1M-32M的2的幂次方。 默认2048分区域。 每个区域region标记为:E区为Eden,S区为Survivor区,O区为Old年老代,H区为巨型区域。 存活的对象从一个区域,转移到另一个区域。 H区,巨型区域超过50%标准的区域对象而设计,一个H区装不下会使用连续的H区,找不到连续的H区会启动Full GC。

线程池/多线程

  • 第9题.有哪几种常用的线程池。 (1)newCachedThreadPool创建一个可缓存线程池。 (2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数。 (3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 (4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务。

  • 第10题.什么情况下使用Runnable和Thread创建线程,Runnable和Callable的区别。 Runnable和Callable的区别:

  1. Callable(重写)的方法是call(),Runnable(重写)的方法是run()。
  2. Callable的任务执行后可返回值,而Runnable不能返回值的。
  3. Call方法可以抛出异常,run方法不可以。
  4. 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。
  • 第11题. 线程方法中的异常如何处理,父线程可以捕获到吗。 方法1.子线程的Runnable类的run方法中,try-catch-finally中捕获异常,父线程无法捕获异常。 方法2.为线程设置“未捕获异常处理器”UncaughtExceptionHandler。 方法3,推荐使用.子线程Callable的call方法里,抛出异常,父线程的Future的get方法捕获异常。 UncaughtExceptionHandler:异常没有在try-catch内,导致线程中断。

  • 第15题:介绍下线程池,阻塞队列的用法,无界队列真的无界吗?BlockingQueue,任务队列。14-37 ArrayBlockingQueue:基于数组结构的有界阻塞队列,先进先出原则对元素进行排序。插入和删除元素时无额外对象实例。生产者和消费者持有同一个锁。 LinkedBlockingQueue:基于链表结构的阻塞队列,先进先出排序元素,吞吐量高于ArrayBlockingQueue。有界限队列。生产者和消费者持有不同锁。 SynchronousQueue:不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue。公平模式/内部FIFO队列与非公平锁/出现饥饿。 PriorityBlockingQueue:基于优先级的无界阻塞队列。不阻塞生产者,阻塞消费者。生产者不能快于消费者消费,不然耗尽堆内存,内部控制线程同步的锁采用的是公平锁。 线程池的参数:核心线程数coreSize、任务队列/阻塞队列BlockingQueue,最大线程数maxSize,拒绝策略RejectHander。

线程池的拒绝任务策略。4拒绝策略/饱和策略: AbortPolicy/默认,无法处理新的任务时抛出异常。 CallerRunsPolicy:只用调用者所在线程来运行任务。 DiscardOldestPolicy:丢弃队列里最久的一个任务,并执行当前任务。 DiscardPolicy:不处理丢弃。

第6题:线程安全(阻塞同步,非阻塞同步,无同步)。 阻塞同步:同一时刻只有一个线程访问资源。 synchronized,Lock类(ReentrantLock类,ReentrantReadWriteLock类)。 非阻塞同步:检测冲突的乐观锁。乐观锁的CAS算法。AtomicInteger类。 无同步方案:?

线程安全产生的原因。 多个线程同时操作共享变量。

第6题:Thread.sleep()方法唤醒后是否需要重新竞争? 不需要,sleep()方法不释放对象锁。 Object.wait()释放对象锁。

第8题:Thread.join()方法。 1.使线程排队运行。2.内部使用wait方法实现。3.释放锁。

第13题:描述完整的线程池执行的流程。 核心线程数 -> 任务队列->最大线程数-拒绝策略 提交任务,核心线程是否有空闲,如果核心线程没有空闲查看核心线程池是否满了,核心线程池没满则创建线程执行任务,核心线程池满了则任务放入任务队列, 如果任务队列满了则判断是否达到最大线程数,没有达到最大线程池数则创建线程执行任务,如果达到最大线程池数则按拒绝策略拒绝任务。 任务队列/阻塞队列/BlockingQueue:ArrayBlockingQueue,LinkedlistBlockingQueue,PriorityBlockingQueue,SynchronizedBlockingQueue 拒绝策略:AbortPolicy,CallerRunsPolicy,DiscardPolicy,OldestDiscardPolicy

怎么实现一个线程安全的计数器? AtomicInteger。代码。。。

第6题:java的线程安全类?14-65 1)通过synchronized实现的线程安全类:HashTable,Vector,StringBuffer,Timer,TimerTask 2)AtomicXXX原子类:AtomicInteger,AtomicLong。通过Unsafe类的Native方法实现。 3)BlockingQueue 接口继承了 BlockingDeque:BlockingQueue(ArrayBlockingQueue,LinkedBlockingQueue,PriorityBlockingQueue),BlockingDeque(LinkedBlockingDeque)。通过final的ReentrantLock加锁实现。 4)CopyOnWriteArrayList和CopyOnWriteArraySet:CopyOnWriteArraySet内部是CopyOnWriteArrayList,CopyOnWriteArrayList底层是ReentrantLock加锁。CopyOnWriteArrayList的线程安全是每次创建副本修改副本重新发布副本。 5)ConcurrentXXX:ConcurrentHashMap,ConcurrentSkipListSet,ConcurrentSkipListMap。 6)ThreadPoolExecuror:ReentrantLock显示加锁。 7)Collections的synchronizedCollection(Collection c):synchronized加锁。

第2题:实现线程安全的方式?ThreadLocal原理? 实现线程安全的方法有:synchronized,Lock类,分布式锁。 分布式锁实现方法有:Redis,mysql,Zookeeper。 ThreadLocal原理? ThreadLocal:用共享变量存储线程私有数据。 每个Thread的对象都有一个ThreadLocal.ThreadLocalMap,ThreadLocalMap是Entry类型的数组,,当创建一个ThreadLocal的时候,就会将该ThreadLocal对象添加到该Map中,其中键就是ThreadLocal,值可以是任意类型。 在该类中,我觉得最重要的方法就是两个:set()和get()方法。当调用ThreadLocal的get()方法的时候,会先找到当前线程的ThreadLocalMap,然后再找到对应的值。set()方法也是一样。

1、多线程有什么用? 发挥多核 CPU 的优势

2、线程和进程的区别是什么? 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间, 一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就

3、Java 实现线程有哪几种方式? 1、继承 Thread类实现多线程 2、实现 Runnable接口方式实现多线程。run方法 3、使用 ExecutorService、Callable、Future 实现有返回结果的多线程

4、启动线程方法 start()和 run()有什么区别? start方法,多线程执行。 run方法,串型执行。

5、怎么终止一个线程?如何优雅地终止线程? stop 终止,不推荐。 interrupt方法。 interrupted方法。

7、线程中的 wait()和 sleep()方法有什么区别? sleep 方法不会放弃这个对象的监视器,wait 方法会放弃这个 对象的监视器 。 wait()是Object类的方法, sleep()是Thread类的方法。

10、多线程之间如何进行通信? wait,notify/notifyAll方法,Object类的方法。

11、线程怎样拿到返回结果? 实现Callable接口/run()异步执行的任务,Future获取执行结果。

13、新建 T1、T2、T3 三个线程,如何保证它们按顺序执行? 用join 方法。 join()是Thread类的。

16、常用的几种线程池并讲讲其中的工作原理。 线程池核心类 :在 java.util.concurrent 包中我们能找到线程池的定义,其中 ThreadPoolExecutor 接口的实现用于创建线程池。四种线程池的创建: newCachedThreadPool创建一个可缓存线程池 newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数。 (3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 (4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务。 ExecutorService pool = new newFixedThreadPool();

17、线程池启动线程 submit()和 execute()方法有什么不同? submit()返回值/Future获取结果,execute()无返回值。 execute 没有返回值,如果不需要知道线程的结果就使用 execute 方法,性能会好很多。 submit 返回一个 Future 对象,如果想知道线程结果就使用 submit 提交,而且它能在主线 程中通过 Future 的 get 方法捕获线程中的异常。

19、什么是活锁、饥饿、无锁、死锁? 活锁:拿到资源却相互释放不执行。 死锁:多个线程相互占用对方的资源的锁。 饥饿:低优先级线程无法得到执行。 无锁:无锁典型的特点就是一个修改操作在一个循环内进行 ,CAS即无锁的实现。

20、并发编程的三要素?什么是原子性、可见性、有序性? 原子性是指一个线程的操作是不能被其他线程打断,同一时间只有一个线程对一个变 量进行操作。 可见性是指某个线程修改了某一个共享变量的值,而其他线程是否可以看见该共享变 量修改后的值。 有序性:按代码顺序执行的,没有指令重排序。

21、什么是守护线程?有什么用? 什么是守护线程?与守护线程相对应的就是用户线程,守护线程就是守护用户线 程,当用户线程全部执行完结束之后,守护线程才会跟着结束。也就是守护线程必 须伴随着用户线程,如果一个应用内只存在一个守护线程,没有用户线程,守护线 程自然会退出。

22、一个线程运行时发生异常会怎样? 如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler() 是用于处 理未捕获异常造成线程突然中断情况的一个内嵌接口。

23、线程 yield()方法有什么用? Yield 方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。它是一个 静态方法而且只保证当前线程放弃 CPU 占用而不能保证使其它线程一定能占用 CPU,执行 yield()的线程有可能在进入到暂停状态后马上又被执行。

24、什么是重入锁? 所谓重入锁,指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本 对象上的锁,而其他的线程是不可以的。

25、Synchronized 有哪几种用法? 锁类、锁方法、锁代码块。

26、Fork/Join 框架是干什么的? 大任务自动分散小任务,并发执行,合并小任务结果。

27、线程数过多会造成什么异常? 线程过多会造成栈溢出,也有可能会造成堆异常。

29、什么是 CAS 算法?在多线程中有哪些应用。 比较-交换。预期值 A 和内存值 V 相同时,才会将内存值修改为 B 并 返回 true,否则什么都不做并返回 false。当然 CAS 一定要 volatile 变量配合,这样才能保 证每次拿到的变量是主内存中最新的那个值 。

30、怎么检测一个线程是否拥有锁? java.lang.Thread类的#holdsLock 方法

31、Jdk 中排查多线程问题用什么命令? jstack 。

32、线程同步需要注意什么? 1、尽量缩小同步的范围,增加系统吞吐量。 2、分布式同步锁无意义,要使用分布式锁。 3、防止死锁,注意加锁顺序。

33、线程 wait()方法使用有什么前提? 要在同步块中使用。

34、Fork/Join 框架使用有哪些要注意的地方? 如果任务拆解的很深,系统内的线程数量堆积,导致系统性能性能严重下降。 如果函数的调用栈很深,会导致栈内存溢出。

35、线程之间如何传递数据? 通过在线程之间共享对象就可以了,然后通过 wait/notify/notifyAll、 await/signal/signalAll 进行唤起和等待, 比方说阻塞队列 BlockingQueue 就是为线程之 间共享数据而设计的 。

36、保证"可见性"有哪几种方式? synchronized 和 viotatile 。

37、说几个常用的 Lock 接口实现锁。 ReentrantLock、ReadWriteLock->ReentrantReadWriteLock 。

38、ThreadLocal 是什么?有什么应用场景? ThreadLocal 的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少 同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。 用来解决数据库连接、Session 管理等。

39、ReadWriteLock 有什么用? ReadWriteLock 是一个读写锁接口,ReentrantReadWriteLock 是 ReadWriteLock 接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥, 读和写、写和读、写和写之间才会互斥,提升了读写的性能。

40、FutureTask 是什么? FutureTask 表示一个异步运算的任务,FutureTask 里面可以传入一个 Callable 的具体实现 类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作。

41、怎么唤醒一个阻塞的线程? 调用了 wait()/notify、sleep()/interrupt或者 join()方法而导致的阻塞,可以中断线程,并且 通过抛出 InterruptedException 来唤醒它。 如果线程遇到了 IO 阻塞,无能为力,因为 IO 是操作系统实现的,Java 代码并没有办法直接接触到操作系统。

42、不可变对象对多线程有什么帮助? 不可变对象保证了对象的内存可见性,对不可变对象的读取不需要进行额外的同步手段,提 升了代码执行效率。

44、Java 中用到了什么线程调度算法? 抢占式。一个线程用完 CPU 之后,操作系统会根据线程优先级、线程饥饿情况等数据算出 一个总的优先级并分配下一个时间片给某个线程执行。

45、Thread.sleep(0)的作用是什么? 手动触发一次操作系统分配时间片 。

48、Hashtable 的 size()方法为什么要做同步? 同一时间只能有一条线程执行固定类的同步方法,但是对于类的非同步方法,可以多条线程 同时访问。所以,这样就有问题了,可能线程 A 在执行 Hashtable 的 put 方法添加数据, 线程 B 则可以正常调用 size()方法读取 Hashtable 中当前元素的个数,那读取到的值可能 不是最新的 。

49、同步方法和同步块,哪种更好? 同步块,同步的范围越小越好。

50、什么是自旋锁? 当前线程在循环体内执行,条件被外部线程改变时进入临界区。 自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区。 循环体,while关键字。

51、Runnable 和 Thread 用哪个好? Java 不支持类的多重继承,但允许你实现多个接口。 所以如果你要继承其他类,也为了减 少类之间的耦合性,Runnable 会更好。

52、Java 中 notify 和 notifyAll 有什么区别? notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。 而 notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行。

53、为什么 wait/notify/notifyAll 这些方法不在 thread 类里面 ? JAVA 提供的锁是对象级的而不 是线程级的

54、为什么 wait 和 notify 方法要在同步块中调用? Java API 强制要求,否则代码会抛出 IllegalMonitorStateException 异常。

55、为什么你应该在循环中检查等待条件? 如果不在循环中检查等待条件,程序就 会在没有满足结束条件的情况下退出。

56、Java 中堆和栈有什么不同? 堆:线程共享。 栈:线程私有。

57、你如何在 Java 中获取线程堆栈? jstack命令获取线程堆栈,传入线程id。 jps命令找到线程id。

58、如何创建线程安全的单例模式? 单例模式:JVM 内存中只存在一个类的对象实例 分类 1、懒汉式 类加载的时候就创建实例。 2、饿汉式 使用的时候才创建实例。

59、什么是阻塞式方法? 阻塞式方法是指程序会一直等待该方法完成期间不做其他事情,ServerSocket 的 accept() 方法就是一直等待客户端连接。这里的阻塞是指调用结果返回之前,当前线程会被挂起,直 到得到结果之后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。

60、提交任务时线程池队列已满会时发会生什么? 当线程数小于最大线程池数 maximumPoolSize 时就会创建新线程来处理,而线程数大于 等于最大线程池数 maximumPoolSize 时就会执行拒绝策略。

第5题:线程池的调优策略?14-64 最大线程数:CPU密集型:NCPU+1,IO密集型:2NCPU 非核心线程创建时机:等到等待队列满才创建新线程,来不及。给等待队列一个阀值,触发阀值直接创建新线程,防止任务过度堆积。

  • 第2题:线程的几种状态?状态流转图? New:调用start方法称为Runnable状态。 Runable:抢占CPU后称为Running状态。 Running:调用yield方法后称为Runnable状态,调用join或者sleep方法后称为Blocked状态,正常退出或者异常退出后称为Dead状态。 Blocked:join或者wait方法完成,IO完成后称为Runnable状态。 Dead:

第3题:wait和sleep区别? 1.wait释放锁,sleep不释放锁。 2.wait方法是object类,sleep方法是Thread类。 3.wait和notify只能在同步块内使用,sleep方法随处可用。 4.sleep必须捕获异常。

  • 28.几种线程池区别。 四种线程池的创建: (1)newCachedThreadPool创建一个可缓存线程池 (2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数。 (3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。 (4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务。

//创建线程池 ExecutorService pool = new newFixedThreadPool(); 。。。

  • 23.并发工具包有哪些,具体怎么用。4个 1.CountDownLatch。主线程等待其他一组线程完成操作,再继续执行。CountDownLatch是通过一个计数器来实现的,计数器的初始值为需要等待线程的数量。调用应用场景:倒数计时器。 2.CyclicBarrier。让一组线程到达一个同步点时被阻塞,直到最后一个线程到达同步点时,屏障才会开门,所有被拦截的线程才会继续运行。CyclicBarrier是通过一个计数器来实现的,计数器的初始值为需要等待线程的数量。 3.信号量Semaphore。通过控制一定数量的许可(permit),限制通用资源访问。例如:控制并发的线程数。 acquire()方法获取许可证,release()方法归还许可证,tryAcquire()方法。用于流量控制,特别是公用资源有限的应用场景,比如数据库连接。 4.交换者Exchanger。用于线程间协作。用于线程间数据交换。提供一个同步点,在这个同步点两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据,Exchanger可以用于校对工作的场景。 锁

  • 第12题.synchronized和锁的区别,什么情况下使用synchronized和ReentrantLock.6个 1.synchronized是关键字Lock是类。 2.synchronized代码执行完或者异常后释放锁,Lock在finnally中释放锁。 3.如果阻塞synchronized会一直等待,Lock可以不用一直等待。 4.synchronized无法判断锁状态,Lock可以判断锁状态。 5.synchronized锁类型:可重入,不可中断,非公平。 Lock锁类型:可重入,可判断,公平与非公平。 6.synchronized少量同步下,Lock可大量同步下使用。 可重入锁:线程获得过锁,可以再次获得锁。synchronized,Lock类均为可重入锁。

  • 第14题:基于AQS实现的锁、信号量、计数器原理。AQS:AbstractQueuedSynchronized AQS是一个抽象类,不可以被实例化,它的设计之初就是为了让子类通过继承来实现多样的功能的。 AQS定义了两种资源共享方式:独占和共享。独占有:ReentrantLock。共享有:Semaphore信号量,CountDownLatch计数器。

第1题:JDK涉及到的悲观锁和乐观锁? synchronized,Lock类:悲观锁。 AtomicInteger:乐观锁。

第1题:synchronized锁升级的过程,说了偏向锁到轻量级锁再到重量级锁,它们分别是怎么实现的,解决的是哪些问题,什么时候会发生锁升级。 锁的四种状态,从低级别到高级别:无锁,偏向锁,轻量级锁,重量级锁。 什么是偏向锁?大多数不存在锁竞争,常常同一个线程多次获得同一个锁。 偏向锁优缺点:加解锁无需额外性能消耗,如果竞争的线程多带来额外撤销锁的消耗。 其他线程每次竞争锁需要代价,因为偏向锁不会主动释放锁。 偏向锁是默认开启的,而且开始时间一般是比应用程序启动慢几秒,如果不想有这个延迟,那么可以使用-XX:BiasedLockingStartUpDelay=0;如果不想要偏向锁,那么可以通过-XX:-UseBiasedLocking = false来设置。 什么时候取消偏向锁?如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。 偏向锁:第一次拿到锁的线程,通过CAS在对象头的MarkWord里记录自己的线程ID,以后每次拿锁不需要CAS。 偏向锁升级:如果该线程执行同步代码时发现有其他线程竞争,则偏向锁升级为轻量级锁。 轻量级锁:通过CAS加锁,加锁失败则自旋,自旋一定次数升级为重量级锁,线程被内核挂起。 自旋锁:轻量级锁膨胀为重量级锁之前,线程进入等待队列时,通过自旋尝试获得锁,如果拿不到进入阻塞状态,需要内核来调度。 什么是重量级锁?通过内核操作线程,频繁在内核态与用户态间切换,严重影响性能。

  • java Lock的实现,公平锁,非公平锁。 lock的公平锁和非公平锁是内部的双向链表实现,CLH队列。公平锁从队列头获取。非公平锁则新的线程直接获取锁,当前线程执行完且没有新的线程抢占才换行等待队列CLH队列。

  • Java的Lock底 层实现。14-55 OK Lock类包含:ReentrantLock,ReadWriteLock->ReentrantReadWriteLock。Lock依赖java.util.concurrentAbstractQueuedSynchronizer类。 AQS原理/Lock类原理: 所有请求线程构成CLH队列,当一个线程执行完/lock.unlock()激活后续节点。CLH队列:虚拟队列,不存在实际队列仅存在节点间前后关系。 CLH队列的第一个节点是正在执行的线程,新的线程添加都队列尾部。公平锁:新的线程直接加入到队列尾部。非公平锁:新的线程先尝试获得锁,获取失败增加到队列尾部。

第16题:加锁有什么机制? 看锁的种类。

  • 第5题:Java的悲观锁和乐观锁实现? 乐观锁:AtomicInteger,CAS。 悲观锁:synchronized,Lock类。

  • 第1题:java有哪些锁种类? 1.公平锁,非公平锁:公平锁按照线程申请锁的顺序来获得锁,非公平锁随机获得锁但可能引发优先级反转或饥饿。synchronized是非公平锁,ReentrantLock根据参数设置2.公平锁和非公平锁,默认非公平锁,非公平锁的吞吐量好。 3.可重入锁:线程在方法外获得锁,进入方法内自动获得锁。 4.独享锁和共享锁:独享锁只有一个线程获得锁,共享锁指多个线程可获得锁。synchronized和ReentrantLock是独享锁,ReadWriteLock->ReentrantReadWriteLock是共享锁。 5.互斥锁和读写锁:互斥锁是ReentrantLock,读写锁是ReadWriteLock。 6.乐观锁和悲观锁: 7.偏向锁,轻量级锁,重量级锁:锁只能升级不能降级,为了提高获得锁和释放锁的效率。 8.自旋锁:自旋锁是一直Runnable状态,全程消耗CPU。 9.可中断锁:这里的中断是阻塞等待中中断。synchronized是不可中断锁,ReentrantLock是可中断锁。

第4题:volatile有什么作用,底层原理? volatile作用:1可见性。2禁止指令重排序。 可见性:线程每次从主内存存取变量,而不是线程自己的工作内存。 禁止指令重排序:Java编译器生成指令序列时,插入特定类型的内存屏障指令,禁止处理器重排序指令。

第4题:volatile如何实现指令重排序? 指令重排序:虚拟机层面虚拟机按照自己的规则,后面的代码先于前面的代码执行;前面的代码后于后面的代码执行。硬件层面,CPU接收到一批指令按照自己的规则,在有限的一批指令内重排序。 volatile禁止指令重排序。volatile,插入一个lock前缀指令,相当于内存屏障(内存栅栏)。lock前缀指令/内存屏障3个功能:1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成。2)它会强制将对缓存的修改操作立即写入主存。3)如果是写操作,它会导致其他CPU中对应的缓存行无效。

第5题:synchronized和AQS异同?AQS公平和非公平如何实现? AQS/队列同步器:基于内置的CHL(FIFO)队列完成线程的排队工作,队列的首部为已获取对象锁的线程,新的等待线程加入到尾部,用int型属性表示线程的同步状态。 AQS与synchronized区别: synchronized非公平锁,AQS为非公平锁&公平锁。 synchronized是JVM层面的,AQS是JDK层面的。 synchronized的无法控制,AQS实现比较灵活。 ReentrantLock实例化时传入参数true为公平锁,默认为非公平锁。 非公平锁高吞吐。

  • 24.Lock和Synchronized的区别。 1.synchronized是关键字Lock是类。2.synchronized代码执行完或者异常后释放锁,Lock在finnally中释放锁。3.如果阻塞synchronized会一直等待,Lock可以不用一直等待。4.synchronized无法判断锁状态,Lock可以判断锁状态。5.synchronized锁类型:可重入,不可中断,非公平。Lock锁类型:可重入,可中断,公平与非公平。6.synchronized少量同步下,Lock可大量同步下使用。 什么是可重入锁?线程在方法外已获取到锁,进入方法内自动获取锁,Lock类与synchronized都是可重入锁。 公平锁与非公平锁?非公平锁一上来直接试图获取锁,获取失败则等待。公平锁线程按照顺序获取锁。非公平锁有更高的吞吐量。

容器:Map,Collections(Set,Array,Queue)

  • 第7题.ConcurrentHashMap和Hashtable区别。 ConcurrentHashMap每个数组元素加锁,效率高。 HashTable整个数据加锁,效率低,不推荐使用。

第1题.HashMap实现原理,ConcurrentHashMap实现原理。 HashMap:数组+链表+红黑树,当链表大于8时转为红黑树。 ConcurrentHashMap:在HashMap基础上,每个数组元素加锁。

第一题:HashMap底层的扩容实现。 桶/数组的元素等于负载因子0.75*数组初始长度时,扩容至原来的2倍。

第2题:线程安全的List有哪些? ArrayList对应的高并发类是CopyOnWriteArrayList, HashSet对应的高并发类是 CopyOnWriteArraySet。v结构。 HashMap对应的高并发类是ConcurrentHashMap。kv结构。 CopyOnWriteArrayList读多写少的场景,读取操作不用加锁,是安全的; 写操作时,先copy一份原有数据数组,再对复制数据进行写入操作,最后将复制数据替换原有数据,从而保证写操作不影响读操作。 copy的整个过程是上锁,不会产生多线程安全问题。

ArrayList和LinkedList的插入和访问的时间复杂度? ArrayList:插入时间复杂度O(n)产生移位,访问时间复杂度O(1) LinkedList:访问时间复杂度O(n),插入时间复杂度O(n)

HashMap检测到hash冲突后,将元素插入在链表的末尾还是开头?JDK1.8后末尾插入

  • HashMap的get方法如何实现。 对key可以哈希计算后找到数组的位置i=(n-1) & hash(key),n为数组长度,遍历数组的链表/红黑树找到值。

  • HashMap可以用在什么场景? 存储键值对,非线程安全。

hashcode的作用,HashMap和HashTable的区别。 hashcode函数返回对象的哈希码值。 hashcode作用:寻域,寻找对象在集合中的区域位置,将集合分为不同的区域。 HashTable底层原理:hashtable所有操作都是synchronized,线程安全,效率低。 HashMap:非线程安全。

第12题:HashMap有什么漏洞导致变慢? HashMap的存放的数组位置i是根据hashCode方法计算出,如果hashCode方法不合理导致大量哈希冲突,导致查找时遍历链表/红黑树,导致速度变慢。

第1题:Hashmap的原理,增删的情况后端数据结构如何位移。 HashMap原理:数组+链表+红黑树 HashMap插入:HashMap是无序的,对应的位置里赋值而已,插入后判断是否扩容,如果元素个数超出桶/数组容量的*0.75则扩容至2倍。 HashMap删除:key的hashCode()方法找到对应的桶/数组元素,将链表或者红黑树对应的元素删除。 数组位置:i=(n数组长度-1)& key.hashCode()

第二题:hashmap容量为什么是2的幂次。 位置i=(n数组长度-1) & hash(key) 当2的幂次方位置I的值均匀分布。

第3题:hashset的源码。 HashSet由哈希表实现,底层是简化的HashMap,存储的不是kv数据,而是不重复的数据。 HashMap底层实现:数组+链表/8+红黑树

并发容器有哪些,并发容器和同步容器的区别。14-75 区别:同步容器锁范围大,性能低。同步容器:Vector,HashTable。 并发容器: ConcurrentHashMap:弱一致性迭代器,修改时创建新的实例修改,iterator遍历原来数据。 ConcurrentQueue:ConcurrentLinkedQueue,基于链表,无界 CopyOnWriteArrayList/CopyOnWriteArraySet:写时复制容器, BlockingQueue:ArrayBlockingQueue,LinkedBlockingQueue,SynchronizedQueue,PriorityQueue BlockingQueue阻塞消费者/生产者

hashset底层实现,hashmap的put操作过程。 HashSet基于HashMap实现,数组+链表+红黑树。put操作过程参考HashMap。 数组位置i=(n-1) & key.hashCode(),单个元素/链表/红黑树。 put后判断是否扩容。 题:HashMap的时间复杂度?HashMap中Hash冲突是怎么解决的? 链地址法。链表的上一级结构是什么?数组。Java8中的HashMap有什么变化?红黑树需要比较大小才能进行插入,是依据什么进行比较的?key值。其他Hash冲突解决方式?14-72 开放地址法:发生冲突,寻找下一块未被占用的存储地址。 再散列函数法: 链地址法:数组+链表。HashMap。

  • 第1题:HashMap如何变成线程安全?每种方式的优缺点。 HashTable:效率低。 ConccurrentHashMap:高效。 Synchronized Map:效率低。

  • 第二题:HashMap,ConcurrentHashMap与LinkedHashMap的区别。 LinkedHashMap:HashMap+双向链表,双向链表的顺序默认为插入顺序。 put()时双向链表和数组都插入。

第1题:HashMap的put操作。 table为空/0,扩容resize。 位置 i=(n数组大小-1) & hashCode(key)确定数组的位置i,已有key直接覆盖,判断节点为链表则遍历插入,节点为红黑树插入,长度大于8转为红黑树。 没有key直接插入。 判断++size大于负载因子0.75则扩容。

第1题: HashTable的底层原理,如何保证线程安全? 散列表,存储kv结构。函数都是同步的,kv都不可以是null。映射不是有序的。 两个因子影响性能:加载因子0.75和初始容量。 发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。拉链法。

  • 第6题:AtomicInteger怎么实现原子修改的? CAS+volatile,乐观锁,比较-交换。

  • 第19题.volatile做什么用的,如何实现可见性的。 共享变量的可见性,对volatile变量直接访问主内存,而不是访问线程的本地内存。

  • 第20题.volatile和atomic的区别。 Volatile保证可见性,不保证原子性。atomic可见性和原子性都保证。

  • 第21题.atomic底层是如何实现的。 volatile + CAS。

  • 第1题:HashMap,如何高效实现数据迁移? 用更大的数组,替换原来小的数组,for遍历小数组判断是否需要hash到新位置。

  • 第一题:hashmap与concurrenthashmap的区别 。 数组+链表+红黑树,线程安全。

  • 第7题:HashMap如何解决Hash冲突。 链地址法,数组的元素是链表,1.8红黑树。 Hash冲突:链地址法,再哈希法,开放地址法/冲突则寻找下一个地址存储。

Java基础 Integer与int区别,integer有什么特殊的函数? Integer是int基本数据类型的封装类。 区别:包装类与基本数据类型,默认值null与0,存储对象与值。 Integer函数:进制转换,字符串转数值,左移右移,数学运算,比较。

第22题:反射,反射的3中方式。 什么是反射?对于运行中的类,知道该类的属性和方法。 反射机制?动态获取对象的信息和调用对象的方法。 反射的3中方式? (new Object).getClass() Class.class.getName() Class.forName(“类名”).getName()

第一题:常用的异常类型? 异常类,Throwable类,分为Error和Exception。 Error类分为:虚拟机错误VirtulMachineError类。 虚拟机错误VirtulMachineError类,分为,StackOverFlowError/栈溢出 和 Out Of Memory/内存泄漏 Error。 Exception类,分为,IO Exception 和 RuntimeException。 Throwable->Error->VirtualMachineError->OutOfMemory&StackOverFlowError Throwable->Exception->IOException & RuntimeException

第四题:object类你知道的方法。9个 equals():equals方法和==是不一样的,但在Object中俩一样。子类一般重写equals方法。 hashCode():用于哈希查找。重写了equals方法必须重写hashCode方法。 wait():使当前线程等待该对象的锁。 notify():唤醒该对象傻姑娘等待的某个线程。 notifyAll():唤醒该对象上等待的所有线程。 toString():。 clone()方法:保护方法,实现对象的浅复制。 getClass():获得运行时类型。 finalize():析构函数,释放资源。 getName()

第1题:councurrent包下有哪些?14-74 1.Condition 2.CountDownLatch 3.CyclicBarrier 4.Semephere 5.ReentrantReadWriteLock 6.ReentrantLock 7.Callable 8.线程池ExcutorService

  • 第1题:Java有没有指针? 没有指针,Java 的引用:指向一个对象,引用本身也占用了内存。

题:一个空Object对象占多少内存? 对象头+对象数据+填充,压缩头-12字节,非压缩-16字节对象头。

其他 拆箱装箱的原理。 拆箱:引用类型值转换为基本数据类型。如调用intValue()方法。 装箱:基本数据类型的值转换为引用类型。如调用valueOf()方法。

第7题:单例有多少种写法?有什么区别? 懒汉式单例:加载类时不创建单例对象,第一次调用时创建单例对象。懒汉式单例非线程安全。 class Singleton{ public static volatile Singleton obj = null; private void Singleton(){} public static synchronized Singleton getInstance(){ if(obj==null) obj = new Singleton(); return obj; } } 饿汉式单例:加载类时创建单例对象。饿汉式-单例线程安全。 class Singleton{ public static Singleton obj = new Singleton(); private void Singleton(){} public static Singleton getInstance(){ return obj; } }

待答

第3题:从ConcurrentHashMap一路问到锁&锁优化->LongAdder->伪共享->缓存行填充->cas等诸多技术细节; 锁优化:无锁,偏向锁,轻量级锁,重量级锁。 LongAdder类:JDK8新增,LongAdder类与AtomicLong类的区别:高并发时前者将对单一变量的CAS操作分散为对数组cells中多个元素的CAS操作,取值时进行求和;而在并发较低时仅对base变量进行CAS操作,与AtomicLong类原理相同。 伪共享: 缓存行填充:

  • 第2题:CAS底层实现? CAS的ABA问题:线程1取出A,这时候线程2也取出A,并且线程2将值变成了B,然后线程2又将数据变成A,这时线程1进行CAS操作发现仍然是A,然后线程1操作成功。虽然线程1的CAS操作成功,但是整个过程就是有问题的。比如链表的头在变化了两次后恢复了原值,但是不代表链表就没有变化。 线程1:A 线程2:A-B-A CAS缺点:循环时间长CPU开销大。只能保证一个共享变量的原子操作。引发ABA问题。

第7题:Collections.sort 底层排序方法。

  • 40.死锁的条件及应对措施。 死锁的四个必要条件: 互斥条件:一个资源每次只能被一个进程使用。 请求与保持条件:新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走。 循环等待条件: 若干进程间形成首尾相接循环等待资源 应对死锁:死锁预防是设法⾄至少破坏产⽣生死锁的四个必要条件之⼀。