谈谈并发编程(二)

101 阅读14分钟

8.ReentrantLock

ReentrantLock 是可重入的独占锁,同时只有一个线程可以获得该锁,其他的线程获取该锁则会阻塞而被放入到阻塞队列中。默认是非公平锁。

AQS中的state表示线程获取该锁的次数,state为0当前锁没有被任何线程持有,当一个线程第一次获取该锁则会通过CAS设置state=1;在该线程没有释放锁的前提下,再次获取该锁则会state=2,这就是可重入锁。在该线程释放该锁,则state-1。直到state=0,表示当前线程释放该锁。

8.1 非公平锁: ReentrantLock“非公平”性即体现在这里:如果占用锁的线程刚释放锁,state 置为 0,而排队等待锁的线程还未唤醒时,新来的线程就直接抢占了该锁,那么就“插队”了。举个例子:当前有三个线程 A、B、C 去竞争锁,假设线程 A、B 在排队,但是后来的 C 直接进行 CAS 操作成功了,拿到了锁开开心心的返回了,那么线程 A、B 只能乖乖看着。

/**
 * Sync object for non-fair locks
 */
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() {
        // 设置状态值
        if (compareAndSetState(0, 1))
            // 设置该锁持有者为当前线程
            setExclusiveOwnerThread(Thread.currentThread());
        else
            // 调用AQS的acquire方法
            acquire(1);
    }
​
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}
public final void acquire(int arg) {
    // 调用ReentrantLock重写方法,这个tryAcquire方法需要在自己类定义
    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) {
        // 当state为0 ,则将该锁的状态设置为1
        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;
}

非公平体现:A线程去获取该锁时,发现该锁已经被占用并且还不是A线程占用的,所以返回false,就会被放到阻塞队列中;之后B线程去获取该锁,恰巧该锁被释放,所以该锁就会被B占用。

8.2 公平锁

/**
 * Fair version of tryAcquire.  Don't grant access unless
 * recursive call or no waiters or is first.
 */
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;
}
// 队列中的第一个元素不是当前线程返回true
// 队列中为空或者为队列的第一个元素
public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}
static final class Node {
​
    /**
     * 用于标记一个节点在共享模式下等待
     */
    static final Node SHARED = new Node();
​
    /**
     * 用于标记一个节点在独占模式下等待
     */
    static final Node EXCLUSIVE = null;
​
    /**
     * 等待状态:取消;当前线程因为超时或者中断被取消。这是一个终结态,也就是状态到此为止
     */
    static final int CANCELLED = 1;
​
    /**
     * 等待状态:通知;当前线程的后继线程被阻塞或者即将被阻塞,当前线程释放锁或者取消后需要唤醒后继线程。这个状态一般都是后继线程来设置前驱节点的。
     */
    static final int SIGNAL = -1;
​
    /**
     * 等待状态:条件等待;当前线程在condition队列中。
     */
    static final int CONDITION = -2;
​
    /**
     * 等待状态:传播
     */
    static final int PROPAGATE = -3;
​
    /**
     * 等待状态
     */
    volatile int waitStatus;
​
    /**
     * 前驱节点
     */
    volatile Node prev;
​
    /**
     * 后继节点
     */
    volatile Node next;
​
    /**
     * 节点对应的线程
     */
    volatile Thread thread;
​
    /**
     * 等待队列中的后继节点
     */
    Node nextWaiter;
​
    /**
     * 当前节点是否处于共享模式等待
     */
    final boolean isShared() {
        return nextWaiter == SHARED;
    }
​
    /**
     * 获取前驱节点,如果为空的话抛出空指针异常
     */
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null) {
            throw new NullPointerException();
        } else {
            return p;
        }
    }
​
    Node() {
    }
​
    /**
     * addWaiter会调用此构造函数
     */
    Node(Thread thread, Node mode) {
        this.nextWaiter = mode;
        this.thread = thread;
    }
​
    /**
     * Condition会用到此构造函数
     */
    Node(Thread thread, int waitStatus) {
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

8.3 tryLock

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}
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;
}

使用的非公平锁的方式

8.3 总体步骤

补充:

条件队列:单项链表

AQS——条件队列 - 灰信网(软件开发博客聚合) (freesion.com)

await方法:

使用这个方法必须在一个显式锁的lock和unlock包围的代码块之间;调用该方法后,当前线程会释放锁并被阻塞,直到其他线程通过调用同一个Condition对象的signal或者signalAll方法,再次被唤醒.

signal方法: 唤醒条件队列中的1个线程(await时间最长的线程)。

signalAll方法: 唤醒条件队列中所有的线程,去竞争。

9.ReentrantWriteLock

ReentrantWriteLock读写锁,对于读多写少的场景它比ReentrantLock性能更好。读写锁内部维持了一个WriteLock和ReadLock,依赖Sync实现具体功能。AQS内部维持一个state状态,在读写锁中state的高16位状态表示读状态,低16位表示获取到写锁线程的可重入次数。

9.1 功能

  1. 支持公平和非公平获取锁的方式
  2. 支持可重入,读锁在获取读锁之后就可以获取读锁,写线程在获取了写锁之后既可以再次获取写锁又可以获取读锁;
  3. 读取锁和写入锁都支持锁获取期间的中断;
  4. 可以锁降级:写锁可以转换为读锁,

9.2 写锁的方法

1、void lock()方法

写锁是个独占锁,某时只有一个线程可以获取该锁。如果当前没有线程获取到读锁和写锁,则当前线程可以获取到写锁然后返回。如果当前已经有线程获取到读锁和写锁,则当前请求写锁的线程会被阻塞挂起。另外,写锁是可重入锁,如果当前线程已经获取到了该锁,再次获取只是简单地把可重入次数加1后直接返回。

protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    int c = getState();
    int w = exclusiveCount(c);
    // 表示读锁和写锁被某线程获取
    if (c != 0) {
        // w=0说明低16位为0,表明已经获取到读锁。
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        // 说明当前线程获取到写锁,判断可重入次数
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        // 设置可重入次数
        setState(c + acquires);
        return true;
    }
    // 非公平返回false
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    setExclusiveOwnerThread(current);
    return true;
}

2、void lockInterruptibly()方法

类似于lock方法,它的不同之处在于,它会对中断进行响应,也就是当其他线程调用了该线程的interrupt()方法中断了当前线程时,当前线程会抛出异常InterruptedException异常。

3、boolean tryLock()方法

尝试获取写锁,如果当前没有其他线程持有写锁或者读锁,则当前线程获取写锁会成功,然后返回true。如果当前已经有其他线程持有写锁或者读锁则该方法直接返回false,且当前线程并不会阻塞。如果当前线程已经持有了该写锁则简单增加AQS的状态值后直接返回true。

9.3 读锁的方法

1、void lock()

获取读锁,如果当前没有其他线程持有写锁,则当前线程可以获取读锁,AQS的状态值state的高16位的值会增加1,然后方法返回。否则如果其他一个线程持有写锁,则当前线程会被阻塞。

protected final int tryAcquireShared(int unused) {
    Thread current = Thread.currentThread();
    int c = getState();
    // 判断当前写锁是否被占用,exclusiveCount(c) != 0 表明获取到写锁
    if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current)
        // 将当前线程放入到阻塞队列中
        return -1;
    // 获取读锁计数
    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 != getThreadId(current))
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
            rh.count++;
        }
        return 1;
    }
    return fullTryAcquireShared(current);
}

2、void lockInterruptibly()

类似lock方法,不同之处在于,该方法会对中断进行响应,也就是当其他线程调用了该线程的interrupt()方法中断了当前线程时,当前线程会抛出InterruptedException异常。

3、boolean tryLock()

尝试获取读锁,如果当前没有其他线程持有写锁,则当前线程获取读锁会成功,然后返回true。如果当前已经有其他线程持有写锁则该方法直接返回false,且当前线程并不会阻塞。如果当前线程已经持有了该读锁则简单增加AQS的状态值高16位后直接返回true。

9.4 例子

public class ReentrantWriteLockTest1 {
​
    private static List list = new ArrayList<>();
    private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private static final Lock readLock = lock.readLock();
    private static final Lock writeLock = lock.writeLock();
​
    private static void add(Object o) {
        System.out.println("add 元素:" + o);
        try {
            writeLock.lock();
            list.add(o);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            writeLock.unlock();
        }
    }
​
    private static Object get(int i) {
​
        try {
            readLock.lock();
            list.get(i);
            System.out.println("get 元素:" + list.get(i));
            return list.get(i);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readLock.unlock();
        }
        return null;
    }
​
    private static ExecutorService executorService = Executors.newFixedThreadPool(10);
​
    public static void main(String[] args) throws InterruptedException {
        Thread[] ts = new Thread[10];
        Thread[] td = new Thread[30];
        ReentrantWriteLockTest1 reentrantLockList = new ReentrantWriteLockTest1();
        // 创建10个线程添加元素
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            ts[i] = new Thread(() -> {
                reentrantLockList.add( finalI);
            });
            ts[i].start();
        }
        for (int i = 0; i < 10; i++) {
            ts[i].join();
        }
​
        // 读取元素
        for (int j = 0; j < 30; j++) {
            td[j] = new Thread(() -> {
                System.out.println(reentrantLockList.get(6));
            });
            td[j].start();
        }
        for (int j = 0; j < 30; j++) {
            td[j].join();
        }
    }
}

10.并发队列

阻塞队列和非阻塞队列:阻塞队列通过锁的方式进行实现,非阻塞队列通过CAS进行实现

10.1 ConcurrentLinkedQueue

并发容器,基于单向链表的,非阻塞的无界队列,通过CAS+自旋的方式来实现。

10.1.1 入队操作

public boolean offer(E e) {
    //1 
    checkNotNull(e);
    // 2
    final Node<E> newNode = new Node<E>(e);
    // 3
    for (Node<E> t = tail, p = t;;) {
        Node<E> q = p.next;
        // 4
        if (q == null) {
            // 5 p is last node
            if (p.casNext(null, newNode)) {
                // Successful CAS is the linearization point
                // for e to become an element of this queue,
                // and for newNode to become "live".
                // 6
                if (p != t) // hop two nodes at a time
                    casTail(t, newNode);  // Failure is OK.
                return true;
            }
            // Lost CAS race to another thread; re-read next
        }
        // 7
        else if (p == q)
            // We have fallen off list.  If tail is unchanged, it
            // will also be off-list, in which case we need to
            // jump to head, from which all live nodes are always
            // reachable.  Else the new tail is a better bet.
            p = (t != (t = tail)) ? t : head;
        else
            // 8
            // Check for tail updates after two hops.
            p = (p != t && t != (t = tail)) ? t : q;
    }
}

第一次添加元素:

第二次添加元素:

第三次添加元素:

10.1.2 出队操作

restartFromHead:
for (;;) {
    for (Node<E> h = head, p = h, q;;) {
        // 3 保存当前节点值
        E item = p.item;
        //当前节点有值则CAS变为NULL
        if (item != null && p.casItem(item, null)) {
            // Successful CAS is the linearization point
            // for item to be removed from this queue.
            if (p != h) // hop two nodes at a time
                // 5cas成功标记当前节点并从链表中删除
                updateHead(h, ((q = p.next) != null) ? q : p);
            return item;
        }
        // 6 当前队列为空则返回为null
        else if ((q = p.next) == null) {
            updateHead(h, p);
            return null;
        }
        else if (p == q)
            continue restartFromHead;
        else
            p = q;
    }
}

peek():只获取元素不删除元素,poll():获取元素之后删除元素。oferer()在tail后面添加元素,也就是使用tail.casNext(),而这个方法使用的是CAS操作,只有一个线程会成功,然后失败的线程会循环,重新获取tail,在执行casNext方法。

10.2 ArrayBlokingQueue

在并发过程环境下,调用队列过程中,会根据情况去阻塞调用线程,实现这样带阻塞线程功能的队列。通过锁来实现的,主要用在生产者-消费者模式,用于线程建的数据交换和系统解耦。

BlockingQueue:

如果线程向队列中插入元素,而这个时候,队列满了,就会阻塞这个线程,直到队列空闲;

如果线程从队列中取元素,而这个时候,队列为空,就会阻塞这个线程,直到队列中里面有数据。

方法:

抛异常:add/remove/element

返回boolean:offer、poll、peek——通过加锁

阻塞线程:put take——条件变量实现

阻塞+超时:offer(time)/poll(time)

ArrayBlockingQueue:全局独占锁

队列容量在默认的时候指定,然后不允许修改

队尾插入元素、队首删除元素

队列满了,会阻塞插入元素的线程;队列为空,会阻塞删除元素的线程;

支持公平、非公平锁,默认是非公平。

public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();

10.2.1 offer()

public boolean offer(E e) {
    // 判断是否是null,如果是null,抛异常
    checkNotNull(e);
    // 获取独占锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 如果队列满了则返回false
        if (count == items.length)
            return false;
        else {
            // 插入元素
            enqueue(e);
            return true;
        }
    } finally {
        lock.unlock();
    }
}
/**
 * Inserts element at current put position, advances, and signals.
 * Call only when holding lock.
 */
private void enqueue(E x) {
    // assert lock.getHoldCount() == 1;
    // assert items[putIndex] == null;
    final Object[] items = this.items;
    items[putIndex] = x;
    if (++putIndex == items.length)
        putIndex = 0;
    // count 是可见的,因为加了锁ReentrantLock,加过锁的共享变量都是从主内存中获取。
    count++;
    //  不为空的条件队列唤醒,因为调用take操作阻塞的线程
    notEmpty.signal();
}

10.2.2 put()

public void put(E e) throws InterruptedException {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    // 可被中断
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            // 加入到条件队列中
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}

10.2.2 poll():从队列中头部获取元素并移除,如果队列返回为空则返回为null,该方法是不阻塞的。

public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 当前队列为空则返回null,否则调用dequeue()获取
        return (count == 0) ? null : dequeue();
    } finally {
        lock.unlock();
    }
}

10.2.3 take():如果队列为空则阻塞当前线程直到队列不为空后返回元素。

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

10.2.4 peek():获取头部元素但是不从队列中移除它,如果队列为空则返回null,该方法是不阻塞的。

public E peek() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return itemAt(takeIndex); // null when queue is empty
    } finally {
        lock.unlock();
    }
}

10.3 LinkedBlockingQueue:近似有界阻塞队列,基于链表实现

LinkBlockingQueue和ArrayBlockingQueue

1.底层数据结构不同;

2.锁的不同:ArrayBlockingQueue 是一把全局锁;LinkedBlockingQueue 有两把锁,一把是控制入队的putLock,另一把是控制出队的takeLock;

3.队列大小不同;

4.ArrayBlockingQueue可以指定公平、非公平策略,但是LinkBlockingQueue不可以;

10.3.1 offer():队列中有元素则返回,如果队列已满则丢弃当前元素返回false。

public boolean offer(E e) {
    if (e == null) throw new NullPointerException();
    final AtomicInteger count = this.count;
    // 如果队列满则丢弃将要放入的元素,然后返回false
    if (count.get() == capacity)
        return false;
    int c = -1;
    Node<E> node = new Node<E>(e);
    final ReentrantLock putLock = this.putLock;
    putLock.lock();
    try {
        if (count.get() < capacity) {
            enqueue(node);
            c = count.getAndIncrement();
            // 判断新元素入队后还有空闲空间,则唤醒notFull条件队列里面调用notFull的await()方法。
            if (c + 1 < capacity)
                notFull.signal();
        }
    } finally {
        putLock.unlock();
    }
    // 队列中至少有一个元素
    if (c == 0)
        signalNotEmpty();
    return c >= 0;
}
private void signalNotEmpty() {
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        //  激活条件队列notEmpty里边因为调用notEmpty的await方法,调用条件队列需要获取相对应的锁。
        notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
}

10.3.2 put():向队列尾部插入一个元素,如果队列中有空闲则插入后直接返回,如果队列已满则阻塞当前线程,直到队列中有空闲插入成功后返回。

public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    // Note: convention in all put/take/etc is to preset local var
    // holding count negative to indicate failure unless set.
    int c = -1;
    Node<E> node = new Node<E>(e);
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    putLock.lockInterruptibly();
    try {
        /*
         * Note that count is used in wait guard even though it is
         * not protected by lock. This works because count can
         * only decrease at this point (all other puts are shut
         * out by lock), and we (or some other waiting put) are
         * signalled if it ever changes from capacity. Similarly
         * for all other uses of count in other wait guards.
         */
        while (count.get() == capacity) {
            notFull.await();
        }
        enqueue(node);
        c = count.getAndIncrement();
        if (c + 1 < capacity)
            notFull.signal();
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        signalNotEmpty();
}

10.3.3 poll():从队列头部获取并移除一个元素,如果队列为空则返回null

public E poll() {
    final AtomicInteger count = this.count;
    if (count.get() == 0)
        return null;
    E x = null;
    int c = -1;
    final ReentrantLock takeLock = this.takeLock;
    // 其他线程在调用take或者poll则会被阻塞挂起
    takeLock.lock();
    try {
        if (count.get() > 0) {
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        }
    } finally {
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull();
    return x;
}

10.3.4 take():获取队列头部元素并从队列中移除他们,如果队列为空则阻塞当前线程直到队列不为空然后返回元素。如果阻塞时被其他线程设置了中断标识,则会抛出异常。

10.3.5 peek():获取队列头部元素但是不从队列里面移除它,如果队列为空则返回为null。

public E peek() {
    if (count.get() == 0)
        return null;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lock();
    try {
        Node<E> first = head.next;
        if (first == null)
            return null;
        else
            return first.item;
    } finally {
        takeLock.unlock();
    }
}

10.3.6 remove()

public boolean remove(Object o) {
    if (o == null) return false;
    // 双重锁,保证线程安全
    fullyLock();
    try {
        for (Node<E> trail = head, p = trail.next;
             p != null;
             trail = p, p = p.next) {
            if (o.equals(p.item)) {
                unlink(p, trail);
                return true;
            }
        }
        return false;
    } finally {
        fullyUnlock();
    }
}
// 元素入队
private void enqueue(Node<E> node) {
    // assert putLock.isHeldByCurrentThread();
    // assert last.next == null;
    // 将插入的元素赋值给last.next节点和last
    last = last.next = node;
}
// 元素出队
private E dequeue() {
    // assert takeLock.isHeldByCurrentThread();
    // assert head.item == null;
    // 获取头部元素
    Node<E> h = head;
    // 获取头部元素的next节点
    Node<E> first = h.next;
    // 回收
    h.next = h; // help GC
    // 将第二个元素设置为头结点
    head = first;
   // 返回元素
    E x = first.item;
    // 设置头结点为空,也就是新的哨兵节点
    first.item = null;
    return x;
}

10.4 PriorityBlockingQueue:带有优先级的无界阻塞对队列。每次出队都返回优先级最高或者最低的元素。其内部使用平衡二叉树来实现的。

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    final ReentrantLock lock = this.lock;
    lock.lock();
    int n, cap;
    Object[] array;
    // 当前队列里边的元素数量大于等于队列大小
    while ((n = size) >= (cap = (array = queue).length))
        // 扩容
        tryGrow(array, cap);
    try {
        Comparator<? super E> cmp = comparator;
        // 默认的比较器为null
        if (cmp == null)
            siftUpComparable(n, e, array);
        else 
            // 自定义比较器
            siftUpUsingComparator(n, e, array, cmp);
        // 将队列中的元素数量+1,
        size = n + 1;
        // 激活被take方法阻塞的线程
        notEmpty.signal();
    } finally {
        lock.unlock();
    }
    return true;
}

10.5 DelayQueue:队列中每个元素都有一个过期时间,当从队列中获取元素时,只有过期元素才会出队列,队列头元素时最快要过期的元素。

public boolean offer(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        q.offer(e);
        if (q.peek() == e) {
            leader = null;
            available.signal();
        }
        return true;
    } finally {
        lock.unlock();
    }
}
public class TestDelay {
​
    static class DelayedEle implements Delayed {
        private final long delayTime; // 延迟时间
        private final long expire;// 到期时间
        private String taskName;// 任务名称
​
        public DelayedEle(long delay, String taskName) {
            delayTime = delay;
            this.taskName = taskName;
            expire = System.currentTimeMillis() + delay;
​
        }
​
        /**
         * 剩余时间=到期时间-当前时间
         *
         * @param unit
         * @return
         */
        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        }
​
        @Override
        public int compareTo(Delayed o) {
            return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
        }
​
        @Override
        public String toString() {
            return "DelayedEle{" +
                    "delayTime=" + delayTime +
                    ", expire=" + expire +
                    ", taskName='" + taskName + ''' +
                    '}';
        }
    }
​
    public static void main(String[] args) throws InterruptedException {
        DelayQueue<DelayedEle> delayedEles = new DelayQueue<>();
        Random random=new Random();
        for (int i = 0; i < 10; i++) {
            DelayedEle ele=new DelayedEle(random.nextInt(500),"task+"+i);
            delayedEles.offer(ele);
        }
​
​
        DelayedEle ele=null;
        for(;;){
            while((ele=delayedEles.take())!=null){
                System.out.println(ele.toString());
            }
        }
    }

11. 线程池

11.1 线程池的优点

当执行大量异步任务的时候线程池能够提供较好的性能,线程一个一个的去new,线程的创建和销毁需要开销的,如果使用线程池,那么线程可实现复用,不需要每次都去创建和销毁线程。

提供线程资源管理的手段,比如可以限制线程个数,动态新增线程数。

11.2 Executor 框架:Executor 是框架的基础,ThreadPoolExecutor是线程池的核心实现类,用来被执行的任务。

11.3 execute():提交任务到线程池进行执行。

用户添加任务到线程池,相当于生产者生产元素;workers线程池中的线程直接执行任务或者从任务队列中获取任务相当于消费者消费元素。

属性标识:

// 声明当前线程池的状态 2.线程池的数量
// 高3位:线程池状态  低29位:线程池的个数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
​
// 线程池的状态:
private static final int RUNNING    = -1 << COUNT_BITS; // 111 代表Running 状态,正常接收任务
private static final int SHUTDOWN   =  0 << COUNT_BITS; // 000 代表shutdown状态,不接受新任务,内部还有阻塞队列中的任务,正在进行的任务也正常处理
private static final int STOP       =  1 << COUNT_BITS; // 001 不接受新任务,也不去处理阻塞队列中的任务,同时还会中断正在执行的任务
private static final int TIDYING    =  2 << COUNT_BITS; // 010 过度的状态,代表的状态线程池即将死去
private static final int TERMINATED =  3 << COUNT_BITS;// 011 // Packing and unpacking ctl
private static int runStateOf(int c)     { return c & ~CAPACITY; }//得到线程池的状态
private static int workerCountOf(int c)  { return c & CAPACITY; }// 得到线程池的状态
private static int ctlOf(int rs, int wc) { return rs | wc; } 
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    //获取工作线程数。 当前线程池的线程个数小于核心线程数,则开启新的核心线程进行执行。
    if (workerCountOf(c) < corePoolSize) {
        // 创建核心线程
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    //如果线程池状态是Running状态,及将任务添加到阻塞队列中,大于核心线程数,
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
         //如果线程池状态不是Running状态,删除任务,并执行拒绝策略
        if (! isRunning(recheck) && remove(command))
            reject(command);
        
        // 如果线程池在Running状态,工作线程为0
        else if (workerCountOf(recheck) == 0)
            // 阻塞队列有任务,没有工作线程,添加一个任务为空的工作线程处理阻塞队列中的任务。
            addWorker(null, false);
    }
    // 如果队列满,新增线程,新增失败则进行拒绝测策略
    else if (!addWorker(command, false))
        reject(command);
}
// 添加工作线程 firstTask:该工作线程要执行的任务
// core  是否时核心线程标识
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        // 获取线程池的状态
        int rs = runStateOf(c);
​
        // 通过双重循环CAS增加线程数
        if (rs >= SHUTDOWN &&
            // rs==shuttdown ,如果不是shuttdown,就代表是stop或者更高状态
            // 任务为空 
            // 阻塞队列为null
            ! (rs == SHUTDOWN && firstTask == null &&! workQueue.isEmpty()))
            return false;
        for (;;) {
            int wc = workerCountOf(c);
            // 当前线程已经大于线程池的最大容量,不去创建了
            if (wc >= CAPACITY ||wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // 将工作线程数+1
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }
    // 工作线程能够启动的标识
    boolean workerStarted = false;
     // 工作线程能够添加的标识
    boolean workerAdded = false;
    Worker w = null;
    try {
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            // 实现workers同步,避免添加任务时,其他线程干掉线程池
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // 获取线程池状态
                int rs = runStateOf(ctl.get());
               // RUUNING 状态,是SHUTTDOWN ,创建空任务工作线程,执行阻塞队列中的任务
                if (rs < SHUTDOWN ||(rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    // 获取线程池的工作线程个数
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        // 任务不为空     如果任务为空,getTask从阻塞队列中获取任务   
        while (task != null || (task = getTask()) != null) {
            w.lock();
            if ((runStateAtLeast(ctl.get(), STOP) ||(Thread.interrupted() &&
        runStateAtLeast(ctl.get(), STOP))) &&!wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

线程池的种类

11.2.1 newFixedThreadPool(内存溢出)

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
​

1.如果运行的线程数小于核心线程数,将创建新的线程去执行任务

2.如果运行的线程数大于核心线程数,则将任务放到任务队列中

3.线程执行完1后,会循环从队列中进行取任务来执行

使用LinkedBlockingQueue?

1.线程不会执行拒绝策略

2.最大线程数、存活时间的参数无效

3.线程池中的线程数不会超过核心线程数

11.2.2 newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

11.2.3 newCachedThreadPool(CPU 100%)

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
// 空闲线程等待时间超过60s,则会终止;非核心线程数为最大线程数,没有容量的SynchronousQueue队列

1.执行offer操作,如果maximumPool中有空闲线程就去执行offer提交的任务。

2.当初始时,maximunPool的没有空闲线程,CachedThreadPool就会去创建一个新线程去执行任务。

3.创建的线程在执行完任务后,会执行poll操作,如果60s之后没有任务要执行,那么该空闲线程会终止。

11.2.4 newSingleThreadExecutor:延迟之后执行任务或者定期执行任务

12.线程同步器

12.1 CountDownLatch:主线程开启多个线程去并去执行任务,并且主线程需要等待所有子线程都完成后才进行汇总的场景;类似于倒计时器。

public class CountDownLatchTest {
​
    private static volatile CountDownLatch countDownLatch=new CountDownLatch(2);
​
    public static void main(String[] args) throws InterruptedException {
​
        ExecutorService executorService= Executors .newFixedThreadPool(2);
​
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    countDownLatch.countDown();
                }
                System.out.println(Thread.currentThread().getName()+"Thread is Over");
            }
        });
​
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    countDownLatch.countDown();
                }
                System.out.println(Thread.currentThread().getName()+"Thread is Over");
            }
        });
        System.out.println("wait all Thread is over");
        countDownLatch.await();
        System.out.println("all is over");
        executorService.shutdown();
    }
}

join调用一个子线程的join方法,该线程会一直阻塞直到子线程运行完毕;而countDownLatch可以在子线程运行的过程中调用await方法让线程返回不一定等到线程结束。

12.1.0 原理分析

public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    // 基于AQS,实际把计数器的值赋值给AQS状态的state的值
    this.sync = new Sync(count);
}

12.1.1 await():调用await线程会阻塞,直到其他线程调用该线程的interrupted方法,会抛出异常返回;或者计数器的值为0。

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    //调用线程interrupted方法
    if (Thread.interrupted())
        throw new InterruptedException();
    // 查看当前计数器值是否为0,为0直接返回,否则进入AQS的队列等待。
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}
// 让当前线程阻塞,涉及AQS
private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

12.1.2 countDown():计数器的值减1,递减后计数器值为0,则唤醒所有因调用await方法而被阻塞的线程;调用await方法会被放入阻塞队列中,带计数器为0就返回。

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}
protected boolean tryReleaseShared(int releases) {
    for (;;) {
        // 获取到AQS内部维护的state状态位
        int c = getState();
        if (c == 0)
            return false;
        // 状态位预减1,注意这里是预减,因为多线程的情况下还要cas来保证原子性。
        int nextc = c-1;
        //cas操作,c是预期值,nextc是改变值,如果在并发的情况下为false的就继续自旋,为true的就判断是否状态为0,为0就要执行唤醒等待线程
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}
// 激活由await 方法而被阻塞的线程
private void doReleaseShared() {
    for (;;) {
         // 获取到head的指向节点,head也有可能为null,因为并没有线程在await
        Node h = head;
        // 判断是否有线程在await
        if (h != null && h != tail) {
              // 能进来的话,就代表head和tail有指向,并且head指向的sentinel节点
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}

12.2 CyclicBarrier :让一组线程达到一个屏障时被阻塞,直到最后一个线程到达时,屏障才会开门,所有被屏障拦截的线程才会去干活。

public class CyclicBarrierTest {
​
    public static void main(String[] args) {
        ExecutorService  executorService= Executors.newFixedThreadPool(2);
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("Task1 start run");
                try {
                    cyclicBarrier.await();
                    System.out.println("Task1 end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });
​
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("Task2 start run");
                try {
                    cyclicBarrier.await();
                    System.out.println("Task2 end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });
​
    }
​
    private static CyclicBarrier cyclicBarrier=new CyclicBarrier(2, new Runnable() {
        @Override
        public void run() {
            System.out.println("merge all task");
        }
    });
}

// 所有线程执行完之后才执行下一步任务

public class CyclicBarrierTest1 {
​
    private static CyclicBarrier cyclicBarrier=new CyclicBarrier(2);
​
    public static void main(String[] args) {
        ExecutorService  executorService= Executors.newFixedThreadPool(2);
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                System.out.println(Thread.currentThread().getName()+"Task1 start run");
                cyclicBarrier.await();
                System.out.println(Thread.currentThread().getName()+"Task2 start run");
                cyclicBarrier.await();
                System.out.println(Thread.currentThread().getName()+"Task3 start run");
                cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });
​
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName()+"Task1 start run");
                    cyclicBarrier.await();
                    System.out.println(Thread.currentThread().getName()+"Task2 start run");
                    cyclicBarrier.await();
                    System.out.println(Thread.currentThread().getName()+"Task3 start run");
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });
​
    }
​
​
}

12.2.1 原理分析

//同步操作锁
private final ReentrantLock lock = new ReentrantLock();
//线程拦截器
private final Condition trip = lock.newCondition();
//每次拦截的线程数
private final int parties;
//换代前执行的任务
private final Runnable barrierCommand;
//表示栅栏的当前代
private Generation generation = new Generation();
//计数器
private int count;
 
//静态内部类Generation
private static class Generation {
  boolean broken = false;
}
public CyclicBarrier(int parties) {
    this(parties, null);
}
public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}
private int dowait(boolean timed, long nanos)
    throws InterruptedException, BrokenBarrierException,
           TimeoutException {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 
        final Generation g = generation;
​
        if (g.broken)
            throw new BrokenBarrierException();
           //如果当前线程被中断会做以下三件事
          //1.打翻当前栅栏
          //2.唤醒拦截的所有线程
         //3.抛出中断异常
        if (Thread.interrupted()) {
            breakBarrier();
            throw new InterruptedException();
        }
        // 每次计数器减1
        int index = --count;
        // 计数器为0
        if (index == 0) {  // tripped
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand;
                // 执行传入参数的线程命令
                if (command != null)
                    command.run();
                ranAction = true;
                //唤醒所有线程并转到下一代
                nextGeneration();
                return 0;
            } finally {
                if (!ranAction)
                    // 执行失败,则打翻屏障
                    breakBarrier();
            }
        }
​
        // loop until tripped, broken, interrupted, or timed out
        for (;;) {
            try {
                // 判断是否是定时等待
                if (!timed)
                    trip.await();
                else if (nanos > 0L)
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
                if (g == generation && ! g.broken) {
                    breakBarrier();
                    throw ie;
                } else {
                    Thread.currentThread().interrupt();
                }
            }
​
            if (g.broken)
                throw new BrokenBarrierException();
            //如果线程因为换代操作而被唤醒则返回计数器的值
            if (g != generation)
                return index;
​
            if (timed && nanos <= 0L) {
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
        lock.unlock();
    }
}
private void nextGeneration() {
    // 唤醒所有的线程
    trip.signalAll();
    //设置技术器为当前需要拦截的线程数
    count = parties;
    // 创建下一代
    generation = new Generation();
}
private void breakBarrier() {
    // 设置屏障打翻
    generation.broken = true;
    // 设置计数器为当前需要拦截的线程数
    count = parties;
    // 唤醒所有的线程
    trip.signalAll();
}

12.3 Semphore:信号量,通过它实现控制同时访问特定资源的线程数量,合理保证资源的使用。通常用于那些资源有明确访问数量限制的场景,常用于限流,数据库连接池;

public class SemaphoreTest {
​
    private static Semaphore semaphore=new Semaphore(0);
​
    public static void main(String[] args) throws InterruptedException {
​
        ExecutorService  executorService= Executors.newFixedThreadPool(2);
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"over");
                semaphore.release();
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"over");
                semaphore.release();
            }
        });
​
        semaphore.acquire(2);
        System.out.println("over");
        executorService.shutdown();
    }
}

12.3.1 原理分析

public Semaphore(int permits) {
    // 非公平锁;把初始令牌数量赋值给同步队列的state状态,state的值就代表当前所剩余的令牌数量
    sync = new NonfairSync(permits);
}
​
12.3.1.1 acquire()

1、当前线程会尝试去同步队列获取一个令牌,获取令牌的过程也就是使用原子的操作去修改同步队列的state ,获取一个令牌则修改为state=state-1。

2、 当计算出来的state<0,则代表令牌数量不足,此时会创建一个Node节点加入阻塞队列,挂起当前线程。

3、当计算出来的state>=0,则代表获取令牌成功。

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}
final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
        // 获取当前的资源数
        int available = getState();
        // 当前的资源数减去需要消耗的资源数,计算剩余的资源数
        int remaining = available - acquires;
        // 没有竞争了
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            // 返回剩余的资源数
            return remaining;
    }
}
/**
 * 公平锁的tryAcquireShared实现方式。
 * 相对于非公平锁多了一个校验:hasQueuedPredecessors()
 */
protected int tryAcquireShared(int acquires) {
    for (;;) {
        // 如果有链表中有排队的对象!返回-1
        if (hasQueuedPredecessors())
            return -1;
        // 获取当前的资源数
        int available = getState();
        // 当前的资源数减去需要消耗的资源数,计算深入的资源数
        int remaining = available - acquires;
        // 只有剩余的资源数小于0(没有竞争了)或者能通过CAS将当前的资源数变为减少后的值(这些线程可以去获取锁或者等待锁),才会返回结果,否则一直循环。
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            // 返回剩余的资源数
            return remaining;
    }
}
12.3.1.2 release()

1、线程会尝试释放一个令牌,释放令牌的过程也就是把同步队列的state修改为state=state+1的过程

2、释放令牌成功之后,同时会唤醒同步队列中的一个线程。

3、被唤醒的节点会重新尝试去修改state=state-1 的操作,如果state>=0则获取令牌成功,否则重新进入阻塞队列,挂起线程。

/**
 * 释放共享锁的逻辑
 */
public final boolean releaseShared(int arg) {
    // 尝试去释放共享锁
    if (tryReleaseShared(arg)) {
        // 唤醒后继的节点并且保证继续传播
        doReleaseShared();
        // 整体返回true,表示释放共享锁成功
        return true;
    }
    // java规范的写法:必须有个返回值,不会执行到这里!
    return false;
}
​
/**
 * 尝试去释放共享锁
 */
protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        // 获取状态:锁的数量,构造方法传入的数量
        int current = getState();
        // 得到如果释放够的总数量
        int next = current + releases;
        // 大于int的最大值,next变为负数!溢出了,抛异常!
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
        // CAS将持有的状态(剩余资源数)设置为新的值
        if (compareAndSetState(current, next))
            return true;
    }
}