1.java线程池
1.java通过Executors提供四种线程池
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
2.红黑树
- 红黑树本质上是一颗二叉查找树。
- 但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。
-
红黑树的这五条性质,保证了它的高度是lgn。1)每个结点要么是红的,要么是黑的。 2)根结点是黑的。 3)每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。 4)如果一个结点是红的,那么它的俩个儿子都是黑的。 5)对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点。
3.ReentrantLock和synchronized
- synchronized是Java关键字,是不可中断锁,是非公平锁,是托管给JVM管理的,lock是Java写的控制锁的代码。
- reentrantLock是实现了lock接口的类,还包括了java.util.concurrent.locks包中有很多Lock的实现类,ReadWriteLock(实现类ReentrantReadWriteLock).它们是具体实现类,不是Java语言关键字。
- reentrantLock可以设置成公平锁,默认情况下是非公平锁,是可中断锁。在finally中进行释放,如果忘记就会有很大的问题,而synchronized是由jvm自动释放的
- {1.synchronized是托管给JVM执行的,而Lock是Java写的控制锁的代码。
2.synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。
3.Lock用的是乐观锁方式。每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
4.ReentrantLock必须在finally中释放锁,否则后果很严重,编码角度来说使用synchronized更加简单,不容易遗漏或者出错。
5.ReentrantLock提供了可轮询的锁请求,他可以尝试的去取得锁,如果取得成功则继续处理,取得不成功,可以等下次运行的时候处理,所以不容易产生死锁,而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以更容易产生死锁。
6.synchronized的话,锁的范围是整个方法或synchronized块部分;而Lock因为是方法调用,可以跨方法,灵活性更大} - 一般情况下都是用synchronized原语实现同步,除非下列情况使用ReentrantLock:{1.某个线程在等待一个锁的控制权的这段时间需要中断 2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程 3.具有公平锁功能,每个到来的线程都将排队等候}
- reentrant 锁意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了
synchronized
的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续)synchronized
块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个synchronized
块时,才释放锁。 - wait(),notify(),notifyAll()是Object的方法,当需要调用这些方法的时候,一般需要对竞争资源加锁,如果不加锁的话,则会报 IllegalMonitorStateException 异常,参考:https://blog.csdn.net/jianiuqi/article/details/53448849;sleep()是Thread的静态方法
4.Synchronized原理
- 偏向锁原理:如果一个线程获得了锁,那么锁就进入偏向模式,此时Mark Word 的结构也变为偏向锁结构,当这个线程再次请求锁时,无需再做任何同步操作,即获取锁的过程,这样就省去了大量有关锁申请的操作,从而也就提供程序的性能。所以,对于没有锁竞争的场合,偏向锁有很好的优化效果,毕竟极有可能连续多次是同一个线程申请相同的锁。但是对于锁竞争比较激烈的场合,偏向锁就失效了,因为这样场合极有可能每次申请锁的线程都是不相同的,因此这种场合下不应该使用偏向锁,否则会得不偿失,需要注意的是,偏向锁失败后,并不会立即膨胀为重量级锁,而是先升级为轻量级锁。下面我们接着了解轻量级锁。
- 轻量级锁:轻量级锁所适应的场景是线程交替执行同步块的场合,如果存在同一时间访问同一锁的场合,就会导致轻量级锁膨胀为重量级锁。
5.Java垃圾收集器
6.内存泄漏和内存溢出,以及解决方案
- 内存泄漏是没有用但依然被引用而导致的没有被回收,内存溢出指的是申请内存时没有足够的空间,提示 out of memory
- 内存溢出的排查
- 检查数据库查询,因为上线后数据量爆炸,比较隐秘,尽量使用分页查询。
- 检查循环创建新实例的地方
- 检查死循环和递归调用
- 检查List,Map使用完后是否清除
- 内存泄漏
- 容器的使用(长生命周期用了短生命周期)
- 具有close()方法的对象使用要及时关闭
- 单例模式中引用了其他的对象实例