java并发工具类(一)

92 阅读6分钟

1 AQS

管程

  1. 管程:让管理共享变量以及操作共享变量支持并发
  2. 互斥:同一时刻只支持一个线程进行操作
  3. 同步:线程之间的协作以及通信

MESA模型

image.png

入口等待队列

多个线程试图进入排队,线程CAS获取锁成功进入入口,其他线程进入入口线程等待实现了线程之间的互斥

共享变量

线程进入管程后可以操作共享变量

条件变量&条件队列

当线程满足对应的条件时,将自己阻塞然后放入条件等待队列,或者从条件队列重新放入等待队列

java针对管程有2种实现

  • 基于object的Monitor机制,用于synchronized内置锁
  • 抽象同步器AQS,用于juc下的Lock锁机制

如何实现一把独占锁

state:是否加锁 0 1 使用cas
等待队列:加锁失败的线程数据,数组or链表
等待唤醒某个线程:
LockSupport.park/unpark

共性逻辑 抽象类
入口等待队列和条件等待队列的出队入队
修改cas操作

等待唤醒机制:
synchronized + object.wait/notify/notifyAll
ReentrantLock + condition.await/signal/signalAll

底层通过操作系统的pthread的api
pthread_create
pthread_cond_wait
pthread_cond_signal

1.1 AQS是什么

java实现的抽象同步框架,可以实现一个依赖状态的同步器

public class MyLock extends AbstractQueuedSynchronizer {

    @Override
    protected boolean tryAcquire(int arg) {
        int state = getState();
        if (state == 0){
            if (compareAndSetState(0, arg)){
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
        }
        else if (Thread.currentThread() == getExclusiveOwnerThread()){
            setState(state + arg);
            return true;
        }
        return false;
    }

    @Override
    protected boolean tryRelease(int arg) {
        int state = getState();
        if (state > arg){
            setState(state - arg);
        }else {
            setExclusiveOwnerThread(null);
            setState(0);
        }
        return true;
    }
}

1 ReentrantLock

公平锁&非公平锁

public void lock() {
    sync.acquire(1);
}

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        //没有获取到锁,添加到入口等待队列,独占模式
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

//非公平锁
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        //每次都尝试去获取锁
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

//公平锁
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 先判断入口等待队列是否存在线程
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}


1.1 加锁源码解读

image.png

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        //获取锁失败,新增一个Node入队
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

private Node addWaiter(Node mode) {
    Node node = new Node(mode);

    for (;;) {
        Node oldTail = tail;
        if (oldTail != null) {
            //新增的node指向老的tail
            node.setPrevRelaxed(oldTail);
            //cas操作将node赋值给tail,node最为新的尾节点
            if (compareAndSetTail(oldTail, node)) {
                oldTail.next = node;
                return node;
            }
        } else {
            //初始化双向链表,注意这里的head节点的成员变量为null
            initializeSyncQueue();
        }
    }
}

final boolean acquireQueued(final Node node, int arg) {
    boolean interrupted = false;
    try {
        //cas循环操作,设置成功跳出逻辑
        for (;;) {
            //获取当前线程Node的prev
            final Node p = node.predecessor();
            //如果prev是head,再次尝试获取一次锁,获取成功就不会阻塞
            //为了减小开销,不直接阻塞
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                return interrupted;
            }
            // 设置prev节点的waitStatus = -1.以便后续唤醒
            if (shouldParkAfterFailedAcquire(p, node))
                 //park 线程进入阻塞,等待唤醒,唤醒后还会继续循环获取锁
                interrupted |= parkAndCheckInterrupt();
        }
    } catch (Throwable t) {
        cancelAcquire(node);
        if (interrupted)
            selfInterrupt();
        throw t;
    }
}

2 ReentrantReadWriteLock

如何设计一把读写锁?

读读共享,写读,写写互斥
互斥: tryAcquire, tryRelease 写锁: state 0 1
共享: tryAcquireShared, tryReleaseShared 读锁 state != 0

两个状态: 读状态,写状态 int state
一个变量控制2种状态? 高低位
00000000 00000000 00000000 00000000 高16位,低16位

读写互斥,加写锁怎么判断是否有读锁,如何实现可重入?
写锁:独占 只会有一个线程占用写锁 +1
读锁:运行多个线程重入 ThreadLocal

image.png

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading
     */
    Lock readLock();

    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing
     */
    Lock writeLock();
}
protected final int tryAcquireShared(int unused) {
    /*
     * Walkthrough:
     * 1. If write lock held by another thread, fail.
     * 2. Otherwise, this thread is eligible for
     *    lock wrt state, so ask if it should block
     *    because of queue policy. If not, try
     *    to grant by CASing state and updating count.
     *    Note that step does not check for reentrant
     *    acquires, which is postponed to full version
     *    to avoid having to check hold count in
     *    the more typical non-reentrant case.
     * 3. If step 2 fails either because thread
     *    apparently not eligible or CAS fails or count
     *    saturated, chain to version with full retry loop.
     */
    Thread current = Thread.currentThread();
    int c = getState();
    //获取写锁,判断是否存在写锁;通过移位操作获取低16位
    if (exclusiveCount(c) != 0 &&
        //锁降级,如果有写锁,线程是自己,就继续获取读锁,否则获取失败
        getExclusiveOwnerThread() != current)
        return -1;
    //获取读锁count
    int r = sharedCount(c);
    if (!readerShouldBlock() &&
        r < MAX_COUNT &&
        compareAndSetState(c, c + SHARED_UNIT)) {
        if (r == 0) {
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            firstReaderHoldCount++;
        } else {
            HoldCounter rh = cachedHoldCounter;
            if (rh == null ||
                rh.tid != LockSupport.getThreadId(current))
                //通过threadLocal保存读锁的重入次数
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
            rh.count++;
        }
        return 1;
    }
    return fullTryAcquireShared(current);
}

** 锁降级**

获取写锁后再获取读锁是可以的

2.1 StampLock

乐观读

1.通过乐观读模式读取数据,读操作校验是否有写锁进入,如果有,就加读锁,没有就不加锁

缺陷

没有实现可重入,公平非公平,没有实现AQS

3 JUC并发容器

3.1 ConcurrentHashMap

jdk1.7

Segment<K,V> 数组 + HashEntry<K,V>数组 + 链表

image.png

segment继承了ReentrantLock类,每次进行写操作时,会对segment对象加锁,保证只有一个线程进行写操作;但是如果此时有多个线程读,我们如何保证线程能够读到写的线程修改后的值;当然是通过对我们的对象以及成员变量加上volatile关键字,被volatile关键字修饰的变量适用于单线程写,多线程读场景,所以在ConcurrentHashMap中就利用了该特性实现了读操作不用加锁

jdk1.8

image.png

在1.8版本,ConcurrentHashMap取消了分段锁,采用CAS加上synchronized关键字去实现变并发写逻辑,当对象的key的hash值对应到table[i]为null时,通过cas操作将Node<K,V>的内存地址更新到数组里边,如果失败,第2次时,发现数组有值,则对该链表加锁,再通过equals方法判断key是否相等,相等就把value赋值给Node的value变量;不相等继续遍历链表,直到链表中lastNode的next为空,将该Node赋值给lastNode的next属性

final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) throw new NullPointerException();
    
    //获取hashCode,并通过移位操作将hashCode的高位参与运算得到新的hash值作为该Node的hash
    int hash = spread(key.hashCode());
    int binCount = 0;
    //一直循环直到Node对象插入成功
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh; K fk; V fv;
        if (tab == null || (n = tab.length) == 0)
            //初始化Node数组
            tab = initTable();
        //table[i] = null CAS操作成功跳出循环,失败进入下一次循环重新判断条件
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
                break;                   // no lock when adding to empty bin
        }
        else if ((fh = f.hash) == MOVED)
            //这里进行扩容操作
            tab = helpTransfer(tab, f);
        else if (onlyIfAbsent // check first node without acquiring lock
                 && fh == hash
                 && ((fk = f.key) == key || (fk != null && key.equals(fk)))
                 && (fv = f.val) != null)
            return fv;
        else {
          // tab[i]不为空,对链表加锁
            V oldVal = null;
            synchronized (f) {
                if (tabAt(tab, i) == f) {
                    if (fh >= 0) {
                        binCount = 1;
                        for (Node<K,V> e = f;; ++binCount) {
                            K ek;
                             // equals相等,更新value
                            if (e.hash == hash &&
                                ((ek = e.key) == key ||
                                 (ek != null && key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            Node<K,V> pred = e;
                            //链表尾部插入Node
                            if ((e = e.next) == null) {
                                pred.next = new Node<K,V>(hash, key, value);
                                break;
                            }
                        }
                    }
                    else if (f instanceof TreeBin) {
                        Node<K,V> p;
                        binCount = 2;
                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                       value)) != null) {
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    }
                    else if (f instanceof ReservationNode)
                        throw new IllegalStateException("Recursive update");
                }
            }
            if (binCount != 0) {
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i);
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    addCount(1L, binCount);
    return null;
}