LockSupport
LockSupport.park() ,LockSupport.unpark(Thread) 可以针对某个线程进行阻塞或者释放。相对比较灵活,并且.unpark(Thread)方法可以提前执行,park()执行到的时候并不会阻塞
面试题
面试题:写一个固定容量同步容器,拥有put和get方法,以及getCount方法, 能够支持2个生产者线程以及10个消费者线程的阻塞调用
public class syncContainer {
private final LinkedList<T> lists = new LinkedList();
private final int MAX=10;
private int count =0;
public synchronized void put(T t){
while (lists.size()== MAX){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
lists.add(t);
this.notifyAll();
}
}
public synchronized T get(){
while (lists.size()==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
T t = lists.removeFirst();
count--;
this.notifyAll();
return t;
}
}
深入解读AQS源码
说实话看源码最好是看能够跑起来的源码,点着看很容易走到死胡同,最好是把代码运行起来,我们追着代码看,这样能够更好的知道他的来龙去脉,然后根据自己的记忆画出他的类依赖关系,以及每个类中的所调用的方法加深记忆。
ReentrantLock 加锁解锁流程图
深入剖析每个细节是如何处理的
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
//CAS操作尝试加锁
if (compareAndSetState(0, 1))
//加锁成功设置当前线程为独占线程
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
}
public final void acquire(int arg) {
//
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
//非公平获取
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取当前线程的state状态 0:是无锁状态
int c = getState();
//如果没有锁
if (c == 0) {
//CAS尝试加锁
if (compareAndSetState(0, acquires)) {
//加锁成功设置为独占线程
setExclusiveOwnerThread(current);
return true;
}
}
//如果当前线程是独占线程
else if (current == getExclusiveOwnerThread()) {
//因为是可重入锁在原来的state上在加1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//更新state
setState(nextc);
return true;
}
//如果没拿到锁返回false
return false;
}
//把线程加入NODE双向链表队列中
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// 设置pred 是最后一个node
Node pred = tail;
//如果pred不是null
if (pred != null) {
node.prev = pred;
//利用CAS更新尾部node
if (compareAndSetTail(pred, node)) {
//把尾部更新成当前node
pred.next = node;
return node;
}
}
//如果tail是null
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
//如果tail是null 初始化一个node链表
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
//如果tail不是null在他后面追加一个node
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取队列的前一个node节点
final Node p = node.predecessor();
//如果p节点是head,尝试去获取锁
if (p == head && tryAcquire(arg)) {
//如果获取到锁说明head已经处理完了,p节点前移编程head
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//已经尝试过获取锁了,但还是失败了(当然有可能是因为p != head)
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
//已经尝试过获取锁了,但还是失败了(当然有可能是因为p != head)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* 前驱节点已经设置了SIGNAL,闹钟已经设好,现在我可以安心睡觉(阻塞)了。
* 如果前驱变成了head,并且head的代表线程exclusiveOwnerThread释放了锁,
* 就会来根据这个SIGNAL来唤醒自己
*/
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* 发现传入的前驱的状态大于0,即CANCELLED。说明前驱节点已经因为超时或响应了中断,
* 而取消了自己。所以需要跨越掉这些CANCELLED节点,直到找到一个<=0的节点
*/
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
*
* 进入这个分支,ws只能是0或PROPAGATE。
* CAS设置ws为SIGNAL
*/
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
解锁的代码自己分析一下,有一个重要的就是可能被加了多个锁,我们要一个一个的去解锁。
VarHandler
JDK9才出现的变量句柄-VarHandler,说白了这个VarHandler能够直接操作二进制代码,能够高效,有兴趣的同学可以自己查阅一下。
引用
- 强引用:只要某个对象与强引用关联,那么JVM在内存不足的情况下,宁愿抛出outOfMemoryError错误,也不会回收此类对象。
- 软引用:java中使用SoftRefence来表示软引用,如果某个对象与软引用关联,那么JVM只会在内存不足的情况下回收该对象。那么利用这个特性,软引用可以用来做缓存。
- 弱引用:java中使用WeakReference来表示弱引用。如果某个对象与弱引用关联,那么当JVM在进行垃圾回收时,无论内存是否充足,都会回收此类对象。
- 虚引用:java中使用PhantomReference来表示虚引用。虚引用,虚引用,引用就像形同虚设一样,就像某个对象没有引用与之关联一样。若某个对象与虚引用关联,那么在任何时候都可能被JVM回收掉。虚引用不能单独使用,必须配合引用队列一起使用。