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

12 篇文章 0 订阅
12 篇文章 0 订阅

问题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;
    }
}

++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; }
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值