1,StringBuild、StringBuffer、String 区别,Integer a= 130 与 Integer Integer b= 130 相等么,120呢?为什么
(-128,127做了缓存,所以是同一个对象,主要是基于效率考虑,防止频繁创建)
2,了解java的集合有哪些
ArrayList,Vector,LinkedList,HashSet(底层HashMap),LinkedHashSet,TreeSet,HashMap,TreeMap
https://blog.csdn.net/lovewebeye/article/details/79573530
3,了解hashmap的实现么?它是如何解决冲突的?为什么扩容是2n次方,为什么不用平衡二叉树
(或者问jdk7 与jdk8有哪些区别,做了哪些优化)
a,底层是是数组+链表的结构,jdk8 引入红黑树,链表节点>8个,就转化为红黑树
b,开发地址法(线性探测、平方探测)、链地址法、再哈希、建立公共溢出区
c,为2的幂-1都是11111结尾的,所以碰撞几率小。如 index =(h & length - 1),而且 &比 %运算效率高
数组的特点:查询效率高,插入,删除效率低。链表的特点:查询效率低,插入删除效率高。
在HashMap底层使用数组加(链表或红黑树)的结构完美的解决了数组和链表的问题,使得查询和插入,删除的效率都很高。
红黑树:不追求"完全平衡",增删都能在三次旋转达到平衡,比平衡二叉树效率高,读取略逊于AVL(查询性能只比相同内容的AVL树最多多一次比较),维护强于AVL,空间开销与AVL类似,内容极多时略优于AVL,维护优于AVL。
4,线程安全的hashMap是哪个?
ConcurrentHashMap,jdk7利用分段锁技术+volatile技术,put加锁,get不需要,比hashtable效率高
jdk8采用CAS+synchronized,放弃分段锁
- 加入多个分段锁浪费内存空间。
- 生产环境中, map 在放入时竞争同一个锁的概率非常小,分段锁反而会造成更新等操作的长时间等待。
- 为了提高 GC 的效率
5,volatile ,解决:变量的内存可见性,基于内存屏障(Memory Barrier)实现。
volatile应用场景:状态标志,一次性安全发布,温度计,安全计数(++i),双重检查(单例模式)
Atomic包里的主要利用volatile + UnSafe.compareAndSet()保证原子性和可见性
6,java中的锁、内存模型、内存区域划分、类加载过程
https://blog.csdn.net/lovewebeye/article/details/105478801
7,垃圾回收机制
7,JUC源码看过么?如:AQS有哪些?
AQS是一个同步框架,通过维护一个int类型的变量state和一个先进先出队列,实现共享资源的访问控制和线程阻塞,其提供了一些基类实现排队和阻塞机制.
- FairSync/NonfairSync (ReentantLock中的、ReentrantReadWriteLock中的)
- Semaphore
- CountDownLatch
- Worker(ThredPoolExecutor类中)
1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:
CountDownLatch等待多个线程完成,不阻塞任务线程;
而CyclicBarrier等待多个线程一起开始,阻塞任务线程;
另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。
2)Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。
3)如果当前线程不是锁的占有者,则NonfairSync并不判断是否有等待队列,直接使用compareAndSwap去进行锁的占用;
如果当前线程不是锁的占有者,则FairSync则会判断当前是否有等待队列,如果有则将自己加到等待队列尾;
CountDownLatch countDownLatch = new CountDownLatch(5);
for(int i=0;i<5;i++){
new Thread(new readNum(i,countDownLatch)).start();
}
countDownLatch.await();
System.out.println("线程执行结束。。。。");
Runnable类似() => {
latch.countDown();
System.out.println("线程组任务"+id+"结束,其他任务继续");
}
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () => {
System.out.println("线程组执行结束");
});
for (int i = 0; i < 5; i++) {
new Thread(new readNum(i,cyclicBarrier)).start();
}
Runnable类似() => {
try {
cyc.await();
System.out.println("线程组任务" + id + "结束,其他任务继续");
} catch (Exception e) {
}
}
Semaphore semaphore = new Semaphore(5);
类似
semaphore.acquire();
System.out.println("工人"+this.num+"占用一个机器在生产...");
Thread.sleep(2000);
System.out.println("工人"+this.num+"释放出机器");
semaphore.release();
AQS核心思想:如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。 AQS使用一个voliate int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。
AQS定义了两种资源获取方式:独占(只有一个线程能访问执行,又根据是否按队列的顺序分为公平锁和非公平锁,如ReentrantLock) 和共享(多个线程可同时访问执行,如Semaphore/CountDownLatch,Semaphore、CountDownLatCh、 CyclicBarrier )。ReentrantReadWriteLock 可以看成是组合式,允许多个线程同时对某一资源进行读。
AQS底层使用了模板方法模式, 自定义同步器在实现时只需要实现共享资源 state 的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在上层已经帮我们实现好了。
8,ThredPoolExecutor 的参数有哪些?
可以问:线程队列满了会出现什么情况,可不可以丢弃掉任务?(考:创建过程,以及拒绝策略)
(corePoolSize,maximumPoolSize,keepAliveTime, TimeUnit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,
RejectedExecutionHandler handler)
1,没达到corePoolSize,则创建新线程
2,到达之后,线程放入队列,阻塞排队
3,队列满后,没达到maximumPoolSize,创建新线程
4,当线程空闲时间达到 keepAliveTime值时,线程会被销毁
(默认>corePoolSize 才销毁,allowCoreThreadTimeOut为true时候,才销毁corePool)
5,TreadFactory 创建线程的工程类。可以通过指定线程工厂为每个创建出来的线程设置更有意义的名字
6,handler 执行拒绝策略的对象。当线程池的阻塞队列已满和指定的线程都已经开启时。
AbortPolicy: 直接拒绝所提交的任务,并抛出 RejectedExecutionException 异常;(默认)
CallerRunsPolicy:只用调用者所在的线程来执行任务;
DiscardPolicy:不处理直接丢弃掉任务;
DiscardOldestPolicy:丢弃掉阻塞队列中存放时间最久的任务,执行当前任务