- Java容器底层原理
- Java高并发内容
- JVM
一. 容器底层原理
- ArrayList
由数组实现,初始化数组长度,每次增加都会变成之前的1.5倍(扩容)。
是线程不安全的,会产生fail-fast保护
fail-fast机制?当方法检测到对象的并发修改,但是不允许这种修改时就会抛出ConcurrentModifictionException异常。
2.Vector
由数组实现,指定扩容或者*2
线程安全,也有fail-fast保护,每一个操作都有锁保护
3.LinkedList
基于链表实现
4.hashSet
底层用hashMap实现。保存的对象实际上是底层维护的Map对象的key,而value则都是同一个Object对象
5.TreeSet
底层是树,实现来自TreeMap。
6.hashMap
底层是数组和链表的组合体-散列,有fail-fast保护
当我们往 HashMap 中 put 元素的时候,先根据 key 的 hashCode 重新计算 hash 值,根据 hash 值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上
7.TreeMap
底层用红黑树的排序二叉树来保存Map中的每一个Entry。每一个Entry都被当成“红黑树”的一个节点。当执行put方法时,将该Entry当成一个新节点,添加到已有的红黑树。在插入时,当做排序二叉树来操作即可。由于红黑树可能失衡,则进行调整fixAfterInsertion()
调整涉及到左旋,右旋,着色三个操作。
8.hashTable
底层是Dictionary,实现了fail-fast机制
key和value都不能为null,会抛出NullPointerException
所有方法几乎都是synchronized
9.LinedHashMap
相比HashMap,其插入的时候是有顺序的,输出的时候可以保持和插入的顺序相同。
底层通过hash表和双向链表来实现
10.ConcurrentHashMap
细分为segment,segment之间允许并发写操作。
二. JAVA高并发内容
- 实现多线程的方式:
Thead类或者Runnable接口
Executor线程池
2.中断线程的方法:
用stop方法容易导致发生死锁
最好使用volatile的布尔类型变量自定义一个中断线程的方法
3.TheadLocal
线程内部的独立变量,可以保证线程安全
注意回收:
TheadLocal.remove()
4.堆和栈?
堆是存放对象,以及线程之间的共享变量的。
栈是线程自己的内存区域,用于存放本地变量,方法参数
5.重入锁和synchronized相比有什么优点?
(1)由开发者来控制加锁和释放锁,比较灵活
(2)可以多次获取同一把锁
(3)可以临时中断等待锁的申请
(4)可以限时申请锁,不至于一直等待
(5)公平锁设定,可以保证不出现饥饿,先来先得
6.信号量semaphore
信号量申请和释放的中间临界区可以允许多个线程访问
7.CountDownLatch和CyclicBarrier
CountDownLatch是减计数法,减到0后无法重置。
CyclicBarrier是加计数,加到指定值后重新从0开始(到达指定值之前是阻塞状态)
8.线程池:
(1)newFixedThreadPool() 固定大小的线程池
(2)newScheduledThreadPool() 计划任务调度
(3)newCachedThreadPool() 缓存型
(4)SingleThreadExecutor() 只有一个线程
使用线程池的好处:
减少在创建和销毁线程上所花的时间以及系统资源的开销
如果不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存
9.锁的优化
(1)减小锁粒度,如hashMap改成ConcurrentHashMap
(2)使用读写锁代替独占锁
(3)减小锁的持有时间
(4)锁粗化,如果连续对同一把锁请求和释放的话,整合这些请求可能会好一些
10.关于ThreadLocal
多线程使用同一对象的副本,互不干扰
底层是ThreadLocalMap,Key是ThreadLocal当前对象,Value就是需要保存的值。使用的是弱引用,在线程结束后,变量就会被回收。
11.CAS无锁算法(原子类的实现原理-AtomicInteger)
CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做
12.如何避免死锁?
(1)采用无锁函数
(2)使用重入锁的中断或者限时等待
13.Fork/Join框架
有些类似于MapReduce的分而治之的思想。Fork就是把任务分解,Join就是等待前面的执行分支执行完毕。
14.并发和并行的区别
并行是真正意义上的同时执行
而并发是任务之间交替执行
15.java线程和操作系统线程的关系?
有相同的线程状态:创建,就绪,执行,阻塞和终止
OS中线程是用户级线程和内核级线程
Java的线程是在JVM中实现的,对OS是不可见的
16.分布式锁的实现
(1)基于数据库实现分布式锁
可以基于锁表,或者排他锁(for update语句)
(2)基于缓存实现,性能较高
redis sentx(设置超时时间来控制锁释放)
(3)基于Zookeeper实现,比较可靠
通过节点变化监听来通知锁获取
没有单点问题
JVM内容
堆内区域:新生代,老年代。新生代是新生的对象会被放置的区域,当对象存活了一定时间后,会被移动到年老代。新生代GC是复制算法,GC比较频繁。老年代一般用标记清除。
新生代一般不要设置过大,容易导致频繁GC,甚至如果老年代装不下年轻代的变量,会导致频繁的full GC,十分耗时