1.讲一个技术时候,最好连带着说出应用场景
2.说算法时,把类比的算法都说出来
3.说java的各种属性时结合版本说,集合类,开发工具类变化比较多,1.9,1.9发生了什么变化,jdk10,jdk13 比如13的时候支持python 语法,string可以多行定义
4.面试官愿意问比较擅长的东西
5.面试官问一个知识点,可以说其他知识点,但不要把知识点说完再说其他知识点,比如在说hashmap的key value 都可以为null 时就把hashtable 不允许为null 说出来 举一反三 触类旁通
6.知识点讲的有趣些,以不同的方式讲述。
**HashMap / ConcurrentHashMap**
**线程不安全的HashMap**
多线程环境下,使用HashMap进行put操作会引起死循环,
导致CPU利用率接近100%----->并发情况下不可用。
**效率低下的HashTable容器**
HashTable容器使用synchronized来保证线程安全,
但在线程竞争激烈的情况下HashTable的效率非常低下。
因为当一个线程访问HashTable的同步方法时,
其他线程访问HashTable的同步方法时,可能会进入阻塞或轮询状态。
e.g. 线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,
并且也不能使用get方法来获取元素,所以竞争越激烈效率越低。
ConcurrentHashMap 锁分段技术
将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
volatile
java内存模型中的可见性,原子性,有序性
1.可见性:指线程间的可见性,一个线程修改的状态对另一个线程是可见的。
--》volatile 修饰的变量不允许线程内部缓存和重排序,即直接修改内存(新值立即同步到主内存),以及每次使用前立即从主存刷新
2.原子性:不可分割。 e.g. a=0 是 a++ = a=a+1 否
3.有序性: volatile(禁止指令重排) Synchronized(同一时刻只运行一个线程) 保证线程间操作有序
声明volatile 的变量,JVM保证了每次读变量都从内存中读,跳过CPU cache
指令重排序:CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理。
**synchronized /reentrantlock**
**同**:
1.加锁方式同步
2.重入锁
3.阻塞式同步
---》线程阻碍/唤醒代价高(操作系统需在用户态与内核态间来回切换)
**功能区别**:
synchronized 是java关键字,是原生语法层面的互斥,需jvm实现,使用方便,解释器保证锁的添加和释放。
reentrantlock在jdk1.5后为api层面的互斥锁,需lock()、unlock() 方法配合try/finnally完成,手工加锁和释放。
锁的细粒度与灵活度:reentrantlock > synchronized
**性能区别**:
synchronized 在+ 偏向锁 、自旋锁 后 性能~~reentrantlock
如何实现可重入性:
Synchronized进过编译,会在同步块的前后分别形成**monitorenter和monitorexit这个两个字节码指令。**
在执行monitorenter指令时,首先要尝试获取对象锁。
若该对象没被锁定,或当前线程已拥有了那个对象锁,把锁的计算器加1。
在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。
若获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。
ReentrantLock是java.util.concurrent包下提供的一套互斥锁;
有3大高级功能:
1.等待可中断,即持有锁的线程长期不释放时,等待可放弃。
通过lock.lockInterruptibly()来实现这个机制。----避免死锁
2.公平锁,多线程等待同一个锁时,必须按照申请锁的时间顺序获得锁。
Synchronized锁非公平锁,但效率高,因无需记录等待时间,无需排队获得锁
ReentrantLock默认的构造函数是创建的非公平锁,
可以通过参数true设为公平锁,但公平锁表现的性能不是很好。
Lock lock = new ReentrantLock(true);
3.锁绑定多个条件
ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们。
synchronized要么随机唤醒一个线程要么唤醒全部线程。
如何实现可重入性:
ReentrantLock在内部使用了内部类Sync来管理锁,所以真正的获取锁是由Sync的实现类控制的。
Sync有两个实现,分别为NonfairSync(非公平锁)和FairSync(公平锁)。
Sync通过继承AQS实现,在AQS中维护了一个private volatile int state来计数重入次数,避免了频繁的持有释放操作带来效率问题。
下面是部分ReentrantLock源码
// Sync继承于AQS
abstract static class Sync extends AbstractQueuedSynchronizer {
...
}
// ReentrantLock默认是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 可以通过向构造方法中传true来实现公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
线程抢锁过程(公平锁实现):
protected final boolean tryAcquire(int acquires) {
// 当前想要获取锁的线程
final Thread current = Thread.currentThread();
// 当前锁的状态
int c = getState();
// state == 0 此时此刻没有线程持有锁
if (c == 0) {
// 虽然此时此刻锁是可以用的,但是这是公平锁,既然是公平,就得讲究先来后到,
// 看看有没有别人在队列中等了半天了
if (!hasQueuedPredecessors() &&
// 如果没有线程在等待,那就用CAS尝试一下,成功了就获取到锁了,
// 不成功的话,只能说明一个问题,就在刚刚几乎同一时刻有个线程抢先了 =_=
// 因为刚刚还没人的,我判断过了
compareAndSetState(0, acquires)) {
// 到这里就是获取到锁了,标记一下,告诉大家,现在是我占用了锁
setExclusiveOwnerThread(current);
return true;
}
}
// 会进入这个else if分支,说明是重入了,需要操作:state=state+1
// 这里不存在并发问题
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 如果到这里,说明前面的if和else if都没有返回true,说明没有获取到锁
return false;
}
从上面可以看出:
(1) 当一个线程在获取锁过程中,先判断state的值是否为0,如果是表示没有线程持有锁,就可以尝试获取锁(不一定获取成功)。
(2) 当state的值不为0时,表示锁已经被一个线程占用了,这时会做一个判断current == getExclusiveOwnerThread()(这个方法返回的是当前持有锁的线程),这个判断是看当前持有锁的线程是不是自己,如果是自己,那么将state的值加1就可以了,表示重入返回即可。
实现原理:
CAS+CLH队列来实现。
ReenTrantLock的实现是一种自旋锁,通过循环调用CAS操作来实现加锁。
参考:详解
**你有什么想了解的?**
技术面:
1.(先了解公司业务,基于简单的了解下,能了解咱们公司的技术架构或者说这个技术栈。
2.会有定期的技术分享或者交流么?
hr面 :
社保公积金怎么交?
**理想:**
没仔细聊过这个问题,目前的个人想法,先踏踏实实走完技术这条路,对技术感兴趣,专业方面,想成为技术方面牛人;生活上,做一个厉害的普通人,尽己所能 做点贡献。
**职业规划:**
职业规划没有那么长,还没考虑2、30年后怎么样,想着在3、5年内把自己的技术上一个台阶,本身对技术感兴趣,想慢慢成为这个技术案方面专家,另外对于业务、对于架构,对于技术的底层知识,也想做一个更深入的研究,期望能在技术方面做出一些优越的贡献。
**有什么优缺点:**
缺点:比较擅长和技术人员沟通,不太擅长和业务人员沟通,但是现在我在改进,意识到不能永远站在技术的角度看问题,也要站在更高的位置考虑问题。
优:太执拗,遇到问题一定要搞明白。