多线程、锁,可重入,同步,原子性,可见性,非阻塞算法

来源:互联网 发布:哈弗福德文理学院 知乎 编辑:程序博客网 时间:2024/05/20 03:47

问题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:用最小的开销实现实现计数器

@ThreadSafepublic 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;    }}

++value是非原子操作(典型的 读取-修改-写回)用synchronized 保证原子性(也可以用ReentrantLock) ,getValue是原子的,可见性问题由volatile保证了。假如不用volatile那个简单的读取也要用锁


问题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<E> {    AtomicReference<Node> head = new AtomicReference<Node>();    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; }    }}




0 0
原创粉丝点击