juc

96 阅读5分钟
juc包中常用工具?

CountDownLatch 发令枪

  • 公平锁
  • 内部成员sync extends AbstractQueuedSynchronizer
  • 不可重复使用
public class CountDownLatchSample {
    static  CountDownLatch countDownLatch = new CountDownLatch(3);
    public static void main(String[] args) throws Exception{
        System.out.println("食堂开饭了...");
        for (int i=0;i<3;i++){
            final int num = i;
            new Thread(()->{
                System.out.println("饭桶"+num+"准备就绪...");
                countDownLatch.countDown();
                try {
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("其他饭桶已就绪,饭桶"+num+"开始吃饭...");
            }).start();
        }
    }
}
食堂开饭了...
饭桶0准备就绪...
饭桶1准备就绪...
饭桶2准备就绪...
其他饭桶已就绪,饭桶2开始吃饭...
其他饭桶已就绪,饭桶1开始吃饭...
其他饭桶已就绪,饭桶0开始吃饭...

CyclicBarrier 循环发令枪

  • 公平锁
  • 内部成员 ReentrantLock(内部成员sync) + Condition
  • 循环使用
public class CyclicBarrierSample {
    static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);

    public static void main(String[] args) throws Exception {
        for (int j = 0;j<3;j++) {
            System.out.println("食堂开第"+j+"顿饭了---");
            for (int i = 0; i < 3; i++) {
                final int num = i;
                new Thread(() -> {
                    System.out.println("饭桶" + num + "准备就绪...");
                    try {
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    } catch (BrokenBarrierException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("其他饭桶已就绪,饭桶" + num + "开始吃饭...");
                }).start();
            }
            TimeUnit.SECONDS.sleep(5);
            System.out.println("第" + j +"顿饭吃完,等待下一顿开饭---");
        }
    }
}
食堂开第0顿饭了---
饭桶0准备就绪...
饭桶1准备就绪...
饭桶2准备就绪...
其他饭桶已就绪,饭桶2开始吃饭...
其他饭桶已就绪,饭桶0开始吃饭...
其他饭桶已就绪,饭桶1开始吃饭...
第0顿饭吃完,等待下一顿开饭---
食堂开第1顿饭了---
饭桶0准备就绪...
饭桶1准备就绪...
饭桶2准备就绪...
其他饭桶已就绪,饭桶2开始吃饭...
其他饭桶已就绪,饭桶0开始吃饭...
其他饭桶已就绪,饭桶1开始吃饭...
第1顿饭吃完,等待下一顿开饭---
食堂开第2顿饭了---
饭桶0准备就绪...
饭桶1准备就绪...
饭桶2准备就绪...
其他饭桶已就绪,饭桶2开始吃饭...
其他饭桶已就绪,饭桶0开始吃饭...
其他饭桶已就绪,饭桶1开始吃饭...
第2顿饭吃完,等待下一顿开饭---

Semaphore

  • 公平锁
  • 内部成员sync extends AbstractQueuedSynchronizer
  • 限流
public class SemaphoreSample {
    static Semaphore semaphore = new Semaphore(2);
    public static void main(String[] args) throws Exception{
        System.out.println("食堂开饭了,来了5个人,只有俩座位...");
        for (int i=0;i<5;i++){
            final int num = i;
            new Thread(()->{
                try {
                    semaphore.acquire();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("还剩"+semaphore.availablePermits()+"个座位...");
                System.out.println("还剩"+semaphore.getQueueLength()+"个饭桶等待座位...");


                semaphore.release();
                System.out.println("饭桶"+num+"吃完饭,让座...");

            }).start();
        }
    }
}
食堂开饭了,来了5个人,只有俩座位...
还剩0个座位...
还剩0个座位...
还剩3个饭桶等待座位...
还剩3个饭桶等待座位...
饭桶0吃完饭,让座...
饭桶1吃完饭,让座...
还剩0个座位...
还剩1个饭桶等待座位...
还剩0个座位...
还剩0个饭桶等待座位...
饭桶3吃完饭,让座...
饭桶2吃完饭,让座...
还剩1个座位...
还剩0个饭桶等待座位...
饭桶4吃完饭,让座...
Exchanger
  • 两个线程间的多次数据交换,如果使用超过两个线程数据交换,则数据交换错乱,且如果线程数为奇数则必有线程阻塞,可以使用多个Exchanger对象来绑定线程对
public class ExchangerSample {
    static Exchanger<String> exchanger = new Exchanger<>();
    public static void main(String[] args) throws Exception{
        for (int i=0;i<2;i++){
            final int num = i;
            new Thread(()->{
                String produceData = "线程"+num+"产生数据";
                try {
                    String consumeData = exchanger.exchange(produceData);
                    System.out.println("线程"+num+"交换获得的数据:" + consumeData);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }).start();
        }
    }
}
线程0交换获得的数据:线程1产生数据
线程1交换获得的数据:线程0产生数据
ReentrantLock
  • 内部成员readLock、writeLock 中的内部成员sync extends AbstractQueuedSynchronizer
  • 互斥锁
  • 公平和非公平
AQS(AbstractQueuedSynchronizer)
  • head Node(pre、next、thread) 指针
  • tail Node(pre、next、thread) 指针
  • state volitile 状态
  • Node 双端队列
AQS为什么使用双端队列?

入队

  • 没有竞争到锁的node要入队队尾addWaiter(),
  • 循环[for(;;)]入队(先指定当前入队node的pre为tail,然后cas设置tail的next)直到成功入队

入队后获取锁 【从后往前找】

  • 入队后获取锁自旋 acquireQueued(addWaiter(),arg)
  • 循环[for(;;)]判定前置节点是不是head,如果是head则尝试获取锁tryAcquire(),成功后当前节点为head
  • 若是前置节点不是head,且waitStatus>0(线程取消等非法状态) 则继续直到找到waitStatus=-1的节点 并设置为当前节点的前置节点,挂起当前节点的Thread

入队后获取锁异常,取消获取锁

  • 若是前置节点不是head,且waitStatus>0(线程取消等非法状态) 则继续直到找到waitStatus=-1的节点 并设置为tail 指针指向这个节点

释放锁 【从前往后找】

  • 找后置节点,一直到找到waitStatus <=0的第一个后置节点直接唤醒其线程

总结: AQS中获取锁 需要从后往前找,释放锁需要从前往后找,双端队列做到o(1)的查找

AQS为什么设置一个哨兵节点(伪节点/虚拟节点)?
  • 简化边界逻辑处理,避免空队列的特殊处理,哨兵节点并没有等待线程
  • 保存锁的状态信息,比如持有者等
AQS公平锁和非公平锁体现?
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;
}
ReentrantReadWriteLock 读写锁
  • 写写阻塞
  • 读写阻塞
  • 读读不阻塞
  • 支持公平和非公平锁
  • LockSupport
常用队列
  • ArrayBlockingQueue【基于数组实现的有界队列】
  • LinkedBlockingQueue【基于链表节点的有界双端阻塞队列SingleThreadPool&FixedThreadPool使用】
  • PriorityQueue【堆存储结构】
  • DelayQueue【延迟任务的无限制阻塞队列】
  • SynchronousQueue【没有容量/容量为1,一个线程插入,一个线程删除,CachedThreadPool使用】,TO C接口的并发任务线程池执行时,队列应该使用这个队列