问题1 什么是Re-entrant Lock
锁的acquire是per thread 的, 而不是per call的,也就是同一个线程再次申请已经获得的锁,直接成功返回。如果是非re-entrant的锁,一个线程试图获取已经获得的锁会死锁,因为当前线程会挂起,没有机会release锁
synchronized的锁和 ReentrantLock都是 Re-entrant Lock
问题2:java已经有了synchronized,为什么还要在jdk1.5中加入Lock系列?
ReentrantLock 和 synchronized有相同的语义,但是有更高的性能,ReentrantLock 使用原子变量来维护等待锁定的线程队列。
synchronized获取锁的时候只能一直等,没有超时机制,也不能被打断,而且锁的获取和释放必须在一个方法内
而ReentrantLock的lock方法和synchronized是等同语义的,还可以通过tryLock等方法有多种选择,并且以接口和类的形式而不是语言feature的形式存在,更好扩展。
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
Lock的确缺点是必须在finally块中 unlock
问题3:AtomicInteger 主要解决原子性,那可见性呢?需要用volatile声明吗?
AtomicInteger 内部维护的那个int值是用volatile声明的,所以也AtomicInteger也保证了可见性,不需要再用volatile声明
注意:可见性保证只有两个办法,就是synchronized和 volatile
问题4:一个int 成员变量,一个方法赋值,一个方法读值,多线程环境下,需要同步吗?
需要同步,或者用volatile,并且读操作也需要同步。赋值和读值是原子的,但是依旧有可见性问题,否则也许读方法永远都不到最新的值。为什么数据库查询也需要事务?也是可见性问题。
问题5:用最小的开销实现实现计数器
@ThreadSafe
public class CheesyCounter {
// Employs the cheap read-write lock trick
// All mutative operations MUST be done with the 'this' lock held
@GuardedBy("this") private volatile int value;
public int getValue() { return value; }
public synchronized int increment() {
return ++value;
}
}
问题6 volatile的经典应用场景
1)对变量的操作是原子的,(注意原子操作只有赋值和读取,即“=”操作符,++value不是)
2)只有一个线程写,这样就不会产生更新丢失问题,
具体的,最经典的应用就是flag,即第一种场景。 比如一个死循环的服务线程,通过外部线程设置 exit 的flag决定是否退出。
问题7 用ReentrantLock代替synchronized后, 需要用wait, notify的时候怎么办?
众所周知,wait和notify必须放在synchronized块里,现在用了Lock了怎么办?答案是Lock的Condition,也就是用了不需要用Object.wait()了
问题8 锁的等待队列,是先申请的线程先获取吗?
ReentrantLock的构造函数有一个参数,指定这个锁是fair的还是unfair的,fair的意思是说按申请锁的先后顺序排队,先到先得,而unfair的锁不保证这一点。默认是unfair的。 而且,内置的synchronized锁是unfair的,也就是其实先申请锁的线程不一定先执行!
fair的锁比较慢,几种并发模式的性能比较 Fair ReentrantLock < synchronized < Unfair ReentrantLock < 非阻塞算法
问题9 非阻塞算法原理
一般基于CAS, (campare and set/swap),用一个while循环,先读取old value, 然后计算新值,在更新的时候看target 变量的值是否还是oldvalue,如果是,说明没有别的线程干扰,执行更新,否则有别的线程更新过,while回去重新来一遍。注意这里的“看target 变量是否还是oldvalue并且更新”是一个原子操作CAS。
public class NonblockingCounter {
private AtomicInteger value;
public int getValue() {
return value.get();
}
public int increment() {
int v;
do {
v = value.get();
while (!value.compareAndSet(v, v + 1));
return v + 1;
}
}
无锁堆栈
class ConcurrentStack {
AtomicReference head = new AtomicReference();
public void push(E item) {
Node newHead = new Node(item);
Node oldHead;
do {
oldHead = head.get();
newHead.next = oldHead;
} while (!head.compareAndSet(oldHead, newHead));
}
public E pop() {
Node oldHead;
Node newHead;
do {
oldHead = head.get();
if (oldHead == null)
return null;
newHead = oldHead.next;
} while (!head.compareAndSet(oldHead,newHead));
return oldHead.item;
}
class Node {
final E item;
Node next;
public Node(E item) { this.item = item; }
}
}