并发编程由浅及深(一)

news2025/1/22 23:46:42

并发编程重要吗?当然重要,因为并发在我们的项目中真实存在,如果你不能充分了解它那么很可能造成严重的生产事故。最近隔壁项目组出了一个问题,每次请求接口之后都发现线程固定增加了5个,而且线程数一直增加没有减少,他们怀疑是中间件的问题,但实际上是因为他们的代码中线程池使用不当造成。所以了解并发是很有必要的。

并发编程由浅及深

  • 1.基础概念
  • 2.Thread
    • 2.1 线程的创建
    • 2.2 线程的中断方法
    • 2.3 线程的等待唤醒机制
  • 3.ThreadLocal(源码)
    • 3.1 new ThreadLocal<>()
    • 3.2 ThreadLocal#set()
      • 3.2.1ThreadLocal#createMap
        • 3.2.1.1 new ThreadLocalMap()
        • 3.2.1.2 ThreadLocal#getMap
    • 3.3 myThreadLocal.get() 方法
      • 3.3.1 map.getEntry(this) 方法
    • 3.4 myThreadLocal.remove() 方法
      • 3.4.1 m.remove(this)
  • 4.ReentrantLock(非公平锁实现源码)
    • 4.1 new ReentrantLock()
    • 4.2 lock.lock()
      • 4.2.1 sync.lock()
        • 4.2.1.1 acquire(1)
          • 4.2.1.1.1 tryAcquire
            • 4.2.1.1.1.1 nonfairTryAcquire()方法
          • 4.2.1.1.2 addWaiter
            • 4.2.1.1.2.1 enq()
          • 4.2.1.1.3 acquireQueued()
            • 4.2.1.1.3.1 shouldParkAfterFailedAcquire()
            • 4.2.1.1.3.1 parkAndCheckInterrupt()
    • 4.3 lock.unlock()
      • 4.3.1AbstractQueuedSynchronizer#release
        • 4.3.1.1 ReentrantLock.Sync#tryRelease
        • 4.3.1.2 AbstractQueuedSynchronizer#unparkSuccessor
  • 5.ArrayBlockingQueue
    • 5.1 ArrayBlockingQueue#ArrayBlockingQueue(int)
      • 5.1.1 ArrayBlockingQueue#ArrayBlockingQueue(int, boolean)
    • 5.2 ArrayBlockingQueue#put
      • 5.2.1 ArrayBlockingQueue#enqueue
      • 5.2.2 AbstractQueuedSynchronizer.ConditionObject#await()
        • 5.2.2.1 AbstractQueuedSynchronizer.ConditionObject#addConditionWaiter
        • 5.2.2.2 AbstractQueuedSynchronizer#fullyRelease
        • 5.2.2.2.1 AbstractQueuedSynchronizer#release
        • 5.2.2.3 AbstractQueuedSynchronizer#isOnSyncQueue
    • 5.2 ArrayBlockingQueue#take
    • 5.2.1 ArrayBlockingQueue#dequeue
  • 6 CountDownLatch
    • 6.1 CountDownLatch#CountDownLatch
    • 6.2 CountDownLatch#await()
      • 6.2.1 AbstractQueuedSynchronizer#acquireSharedInterruptibly
        • 6.2.1.1 CountDownLatch.Sync#tryAcquireShared
        • 6.2.1.2 AbstractQueuedSynchronizer#doAcquireSharedInterruptibly
    • 6.3 CountDownLatch#countDown
      • 6.3.1 AbstractQueuedSynchronizer#releaseShared
      • 6.3.1.1 CountDownLatch.Sync#tryReleaseShared
      • 6.3.1.2 AbstractQueuedSynchronizer#doReleaseShared
      • 6.3.1.2.1 AbstractQueuedSynchronizer#unparkSuccessor

1.基础概念

  1. 并行:两台饮水机,两个人去接水一人使用一台
  2. 并发:一台饮水机,两个人去接水,需要等待一个人接完才行
  3. 异步:不需要等待结果返回
  4. 同步:需要等待结果返回
  5. 线程:是运行在进程内:比如音乐播放器打开之后,在随机播放着音乐 的同时,还可以继续搜索我们想听的音乐。可以理解有两个线程分别处理着音乐播放和音乐搜索
  6. 进程:是一个具体的应用示例:比如打开音乐播放器就是运行了一个进程
  7. 线程上下文切换:cpu调度线程时切换线程同时需要保存线程相关数据,cpu和内存之间

2.Thread

2.1 线程的创建

线程的创建到底有几种?这个答案其实不是固定。要看回答的方向

There are two ways to create a new thread of execution
eg1:new Thread().start()
eg2:R implements Runnable

上面这句话是Thread类上面的注释,也就是官方说是两种,但是实际使用过程中根据根据这两种又有衍生和变化。概括有一下几种

1. new Thread().start();
2. R implements Runnable
	new Thread(R).start();
3. C implements Callable
	new Thread(new FutureTask<>(C)).start();
4. R implements Runnable
	new ThreadPoolExecutor().execute(R);

2.2 线程的中断方法

1. Thread.interrupt() 给线程加一个中断标记
2. Thread.interrupted() 判断线程是否中断,会清除标记
3. Thread.isInterrupted() 判断线程是否中断,不会清除标记

2.3 线程的等待唤醒机制

1. synchronizedwait(),notify()
2. locksupport 的 park(),unpark()
3. condition wait() signal()

3.ThreadLocal(源码)

下面这段代码大概就是我们使用ThreadLocal的方式,接下来将根据这段代码中的方法对ThreadLocal原理进行剖析。

    // 1.new
    ThreadLocal<Map<String,String>> myThreadLocal = new ThreadLocal<>();

    // 2.存值
    Map<String,String> map = new HashMap<>();
    map.put("org","0001");
    myThreadLocal.set(map);

    // 3.获取值
    map = myThreadLocal.get();
    String org = map.get("org");
    System.out.println(org);

    // 4.清空
    myThreadLocal.remove();

3.1 new ThreadLocal<>()

    public ThreadLocal() {
    }

3.2 ThreadLocal#set()

    public void set(T value) {
    	// 获取当前线程
        Thread t = Thread.currentThread();
        // 获取thread对应的map
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
        	// 创建map
            createMap(t, value);
    }

3.2.1ThreadLocal#createMap

    void createMap(Thread t, T firstValue) {
        // 给thread的threadLocals属性赋值
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
3.2.1.1 new ThreadLocalMap()
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
			// 创建初始长度为16的数组
            table = new Entry[INITIAL_CAPACITY];
            // firstKey传的是this 即myThreadLocal对象本身
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            // 数组中存入值:key=myThreadLocal,firstValue=map
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
3.2.1.2 ThreadLocal#getMap
    ThreadLocalMap getMap(Thread t) {
    	// 获取thread中的ThreadLocalMap属性
        return t.threadLocals;
    }

执行完myThreadLocal.set(map),最终结构如下图:
在这里插入图片描述

3.3 myThreadLocal.get() 方法

    public T get() {
        Thread t = Thread.currentThread();
        // 获取thread的属性ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null) {
        	// 获取key为myThreadLocal的Entry 
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                // 返回value即为set存储的map
                return result;
            }
        }
        // 如果当前线程未set值,默认返回null
        return setInitialValue();
    }

3.3.1 map.getEntry(this) 方法

       private Entry getEntry(ThreadLocal<?> key) {
       	   // 和set时一样,使用myThreadLocal对象获取数组的下标
           int i = key.threadLocalHashCode & (table.length - 1);
           Entry e = table[i];
           // 当前下标位置数组值不为null,且key值相等。即为get返回值
           if (e != null && e.get() == key)
               return e;
           else
               return getEntryAfterMiss(key, i, e);
       }

3.4 myThreadLocal.remove() 方法

     public void remove() {
     	// 获取当前程的属性ThreadLocalMap
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
         	// 移除当前myThreadLocal对象所在下标的数组的值
             m.remove(this);
     }

3.4.1 m.remove(this)

        private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            // 获取对象下标的数组值
            for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

4.ReentrantLock(非公平锁实现源码)

下面的源码实现主要列的是非公平锁,公平锁代码和非公平锁差不多所以鼓励大家自己看看。看源码前请大家想象一个场景,现在有两个线程,第一个线程获取锁成功了在执行业务逻辑时,此时第二个线程过来加锁。下面的源码分析的就是上面这样一个场景。

常规使用如下:

 // 1.创建 ReentrantLock 对象
        ReentrantLock lock = new ReentrantLock(); 
        // 2.获取锁
        lock.lock(); 
        try {
           // 业务逻辑
        } finally {
            // 3.释放锁
            lock.unlock(); 
        }

4.1 new ReentrantLock()

    public ReentrantLock() {
    	// 给ReentrantLock属性赋值;NonfairSync实际上就是抽象的队列同步器
        sync = new NonfairSync();
    }

NonfairSync类的继承关系
在这里插入图片描述

4.2 lock.lock()

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

4.2.1 sync.lock()

   final void lock() {
   		// 使用CAS的方式尝试将同步等待队列中的state由0改为1
       if (compareAndSetState(0, 1))
       		// 修改成功代表获取锁成功,将独占线程赋值为当前线程
           setExclusiveOwnerThread(Thread.currentThread());
       else
       		// 1.获取锁
           acquire(1);
   }
4.2.1.1 acquire(1)
    public final void acquire(int arg) {
    	// tryAcquire尝试获取一次锁,如果失败了addWaiter进入队列,然后再次获取锁acquireQueued
        if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
4.2.1.1.1 tryAcquire
	protected final boolean tryAcquire(int acquires) {
			// 非公平获取锁
            return nonfairTryAcquire(acquires);
        }
4.2.1.1.1.1 nonfairTryAcquire()方法
	// acquires从2.1方法中传过来 固定为1
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        // 获取同步等待队列的state属性,默认值为0,如果不为0代表有线程获取锁成功
        int c = getState();
        if (c == 0) {
        	// 如果当前没有线程获取到锁,就直接cas加锁
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // c!=0,当前已有线程获取了锁。判断是不是同一个线程
        else if (current == getExclusiveOwnerThread()) {
        	// 可重入锁实现 通过state值自增判断重入几次
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
4.2.1.1.2 addWaiter
	// node从4.2.1.1传递的是null
    private Node addWaiter(Node mode) {
    	// 构建node节点
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        // 1.将尾节点赋值给变量pred
        Node pred = tail;
        // 2.判断尾节点是否为空,为空则代表链表为空未进行初始化(双向指针构造的)
        if (pred != null) {
        	// 2.1 尾节点不为空,则将当前node节点添加到链表尾部
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 2.2 尾节点为空,则构建链表,并且插入
        enq(node);
        return node;
    }
4.2.1.1.2.1 enq()
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            // 1.当尾节点为空,构造链表(new node()设置为头节点,并且尾节点和头节点指向同一个node)
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
            // 2.尾节点不为空,将当前获取锁失败的线程所构建的node作为尾节点,并且修改双指针指向
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

我们假设thread1,先获取到了锁,那么thread2执行完上面的enq()方法,双向链表形状大概如下图所示
在这里插入图片描述

4.2.1.1.3 acquireQueued()
	// node为获取锁失败的线程构建的node,arg为1
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
            	//1.获取node节点的前置节点
                final Node p = node.predecessor();
                //2 如果前置节点为头节点,则链表中实际只有一个线程在等待。再次尝试获取锁,如果获取到了锁,将当前node设置为head
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //3 判断线程是否应该阻塞,修改node节点waitStatus==-1等待被唤醒,并且阻塞线程
                if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
4.2.1.1.3.1 shouldParkAfterFailedAcquire()

Node有一个volatile int waitStatus属性
默认=0;SIGNAL=-1;CANCELLED=1;CONDITION=-2;PROPAGATE=-3

	// pred:当前线程构建节点的前置节点 node:当前线程构建的节点
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        // 第二次循环waitStatus==-1,因为4.2.1.1.3 有for (;;)所以会再次进入
        if (ws == Node.SIGNAL)
            return true;
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
        	// 第一次循环waitStatus==0,默认走这里。通过cas将0修改为-1
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

执行完shouldParkAfterFailedAcquire方法后,链表状态大概如下图:
在这里插入图片描述

4.2.1.1.3.1 parkAndCheckInterrupt()
	// 底层使用UNSAFE.park将线程阻塞在这里
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

4.3 lock.unlock()

    public void unlock() {
        sync.release(1);
    }

4.3.1AbstractQueuedSynchronizer#release

    public final boolean release(int arg) {
    	// 1.尝试释放锁
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
            	// 2.唤醒等待的后置节点
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
4.3.1.1 ReentrantLock.Sync#tryRelease
		// releases==1
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            // 如果释放锁的线程不等于加锁线程抛异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            // 释放锁。即将独占线程置为null,恢复state=0
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
4.3.1.2 AbstractQueuedSynchronizer#unparkSuccessor
	// 这里的node==head
    private void unparkSuccessor(Node node) {
    	// ws==-1
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        // head节点的下一个节点即为thread2所在节点。唤醒阻塞中的thread2
        if (s != null)
            LockSupport.unpark(s.thread);
    }

5.ArrayBlockingQueue

整个源码逻辑很多,下面梳理的代码,以最简单的情况为例。一个Producer线程向队列中添加元素,一个Consumer线程从队列中获取元素。有了这个前提再看下面的源码会更加容易一些。

常见使用方式如下:

	......省略
    ArrayBlockingQueue<Integer> abq = new ArrayBlockingQueue<>(16);
	......省略
    abq.put(new Random(100).nextInt());
	......省略
    abq.take();

5.1 ArrayBlockingQueue#ArrayBlockingQueue(int)

	// capacity初始容量
    public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

5.1.1 ArrayBlockingQueue#ArrayBlockingQueue(int, boolean)

	// fair是否公平? 默认false
    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        // 创建指定长度的数组
        this.items = new Object[capacity];
        // 创建非公平锁
        lock = new ReentrantLock(fair);
        // 获取condition
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

5.2 ArrayBlockingQueue#put

    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        // 加锁(可中断锁)
        lock.lockInterruptibly();
        try {
        	// count为数组中存的元素个数 相等则将producer线程await在这里
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

5.2.1 ArrayBlockingQueue#enqueue

    private void enqueue(E x) {
        final Object[] items = this.items;
        // putIndex数组下标>>存数下标 默认putIndex==0
        items[putIndex] = x;
        // 循环数组实现。数组存满之后,下标恢复为0
        if (++putIndex == items.length)
            putIndex = 0;
        // count记录数组中存的元素格式
        count++;
        // 队列中有元素之后,唤醒consumer线程进行消费
        notEmpty.signal();
    }

5.2.2 AbstractQueuedSynchronizer.ConditionObject#await()

     public final void await() throws InterruptedException {
         if (Thread.interrupted())
             throw new InterruptedException();
          // 见 5.2.2.1 
         Node node = addConditionWaiter();
         // 这里返回savedState==1
         int savedState = fullyRelease(node);
         int interruptMode = 0;
         // isOnSyncQueue 返回fasle,所以while条件成立
         while (!isOnSyncQueue(node)) {
         	// producer 线程在这里park阻塞
             LockSupport.park(this);
             if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                 break;
         }
         if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
             interruptMode = REINTERRUPT;
         if (node.nextWaiter != null) // clean up if cancelled
             unlinkCancelledWaiters();
         if (interruptMode != 0)
             reportInterruptAfterWait(interruptMode);
     }
5.2.2.1 AbstractQueuedSynchronizer.ConditionObject#addConditionWaiter
    private Node addConditionWaiter() {
    	// lastWaiter默认为null
        Node t = lastWaiter;
        if (t != null && t.waitStatus != Node.CONDITION) {
            unlinkCancelledWaiters();
            t = lastWaiter;
        }
        Node node = new Node(Thread.currentThread(), Node.CONDITION);
        if (t == null)
            firstWaiter = node;
        else
            t.nextWaiter = node;
        lastWaiter = node;
        return node;
    }

第一次执行完addConditionWaiter方法,Node大概是如下结构
在这里插入图片描述

5.2.2.2 AbstractQueuedSynchronizer#fullyRelease
    final int fullyRelease(Node node) {
        boolean failed = true;
        try {
        	// 在5.2中先加锁了,所以这里默认返回savedState==1
            int savedState = getState();
            // 这里if条件成立 直接return 1
            if (release(savedState)) {
                failed = false;
                return savedState;
            } else {
                throw new IllegalMonitorStateException();
            }
        } finally {
            if (failed)
                node.waitStatus = Node.CANCELLED;
        }
    }
5.2.2.2.1 AbstractQueuedSynchronizer#release
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
        	// 前面的代码没有给head赋值 所以这里null==head,这里直接返回true
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
5.2.2.3 AbstractQueuedSynchronizer#isOnSyncQueue
  final boolean isOnSyncQueue(Node node) {
  	  // node前面执行addConditionWaiter方法时,-2==waitStatus 。所以这里直接return fale
      if (node.waitStatus == Node.CONDITION || node.prev == null)
          return false;
      if (node.next != null) // If has successor, it must be on queue
          return true;
      return findNodeFromTail(node);
  }

5.2 ArrayBlockingQueue#take

   public E take() throws InterruptedException {
       final ReentrantLock lock = this.lock;
       // 先加锁
       lock.lockInterruptibly();
       try {
       	   // 当数组中元素数量=0时,让consumer线程await在这里
           while (count == 0)
               notEmpty.await();
           // 队列中有元素时 获取数组元素
           return dequeue();
       } finally {
           lock.unlock();
       }
   }

5.2.1 ArrayBlockingQueue#dequeue

   private E dequeue() {
       final Object[] items = this.items;
       // takeIndex为获取数组时的下标 默认从0开始
       E x = (E) items[takeIndex];
       items[takeIndex] = null;
       // 当取到数组最后一个元素值重置下标
       if (++takeIndex == items.length)
           takeIndex = 0;
       // 没消费一个元素 数组元素个数-1
       count--;
       if (itrs != null)
           itrs.elementDequeued();
       // 当队列中元素被消费后 唤醒producer线程进行put
       notFull.signal();
       return x;
   }

6 CountDownLatch

源码分析的场景如下:第一个线程启动后调用await方法被阻塞,第二个线程调用countDown方法将state置为0,再唤醒第一个阻塞的线程

常见使用方法如下:

   CountDownLatch countDownLatch = new CountDownLatch(1);
   
   countDownLatch.await();
   
   countDownLatch.countDown();

6.1 CountDownLatch#CountDownLatch

  // 这里为了简单,我们示例中假设cont==1
  public CountDownLatch(int count) {
      if (count < 0) throw new IllegalArgumentException("count < 0");
      // Sync extends AbstractQueuedSynchronizer (这一步相当于state=count)
      this.sync = new Sync(count);
  }

6.2 CountDownLatch#await()

    public void await() throws InterruptedException {
    	// 判断state!=0时阻塞线程
        sync.acquireSharedInterruptibly(1);
    }

6.2.1 AbstractQueuedSynchronizer#acquireSharedInterruptibly

   public final void acquireSharedInterruptibly(int arg)
           throws InterruptedException {
       if (Thread.interrupted())
           throw new InterruptedException();
        // 判断state是否等于0 ,因为初始化时我们设置state==1,所以这 -1<0  if成立
       if (tryAcquireShared(arg) < 0)
           doAcquireSharedInterruptibly(arg);
   }
6.2.1.1 CountDownLatch.Sync#tryAcquireShared
   protected int tryAcquireShared(int acquires) {
       return (getState() == 0) ? 1 : -1;
   }
6.2.1.2 AbstractQueuedSynchronizer#doAcquireSharedInterruptibly
private void doAcquireSharedInterruptibly(int arg)throws InterruptedException {
	// addWaiter 在前面ReentrantLock中讲过。主要构建双向链表
   final Node node = addWaiter(Node.SHARED);
   boolean failed = true;
   try {
       for (;;) {
           final Node p = node.predecessor();
           if (p == head) {
           	    // 这里又判断 state是否等于0 ,所以if条件不成立
               int r = tryAcquireShared(arg);
               if (r >= 0) {
                   setHeadAndPropagate(node, r);
                   p.next = null; // help GC
                   failed = false;
                   return;
               }
           }
           // 最终线程会被park方法阻塞在这里。这个方法在ReentrantLock中讲过
           if (shouldParkAfterFailedAcquire(p, node) &&
               parkAndCheckInterrupt())
               throw new InterruptedException();
       }
   } finally {
       if (failed)
           cancelAcquire(node);
   }
}

6.3 CountDownLatch#countDown

  public void countDown() {
  	  // 执行state-1,当sate==0,唤醒阻塞的线程
      sync.releaseShared(1);
  }

6.3.1 AbstractQueuedSynchronizer#releaseShared

 public final boolean releaseShared(int arg) {
 	 // if条件成立
     if (tryReleaseShared(arg)) {
         doReleaseShared();
         return true;
     }
     return false;
 }

6.3.1.1 CountDownLatch.Sync#tryReleaseShared

  protected boolean tryReleaseShared(int releases) {
      for (;;) {
      	  // 这里getState()==1
          int c = getState();
          if (c == 0)
              return false;
          int nextc = c-1;
          // 使用cas将state从1修改为0
          if (compareAndSetState(c, nextc))
              return nextc == 0;
      }
  }

6.3.1.2 AbstractQueuedSynchronizer#doReleaseShared

到这里再看一下前面的node的结构
在这里插入图片描述

private void doReleaseShared() {
        for (;;) {
            Node h = head;
            // 根据前面构建的node的结构,接下来的两个if条件都是满足的
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue; 
                    // 这里唤醒后面的阻塞线程
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

6.3.1.2.1 AbstractQueuedSynchronizer#unparkSuccessor

// 这里node是前面传进来的head
private void unparkSuccessor(Node node) {
   int ws = node.waitStatus;
   if (ws < 0)
       compareAndSetWaitStatus(node, ws, 0);
   // s节点的线程就是我们自己被阻塞的线程
   Node s = node.next;
   if (s == null || s.waitStatus > 0) {
       s = null;
       for (Node t = tail; t != null && t != node; t = t.prev)
           if (t.waitStatus <= 0)
               s = t;
   }
   // 通过unpark唤醒我们前面通过park方法阻塞的线程
   if (s != null)
       LockSupport.unpark(s.thread);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1209811.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java笔试强训】Day11(CM24 最近公共祖先、HJ86 求最大连续bit数)

CM24 最近公共祖先 链接&#xff1a;最近公共祖先 题目&#xff1a; 将一棵无穷大满二叉树的结点按根结点一层一层地从左往右编号&#xff0c;根结点编号为1。现给定a&#xff0c;b为两个结点。设计一个算法&#xff0c;返回a、b最近的公共祖先的编号。注意其祖先也可能是结…

CodeWhisperer 使用经验分享

今天给大家分享一下 Amazon CodeWhisperer 编程工具&#xff08;免费哦&#xff09;&#xff0c;使用这个软件后我的编码质量提升不少&#xff0c;给大家分享一下我的经验。希望大家支持哦。 Amazon CodeWhisperer 是亚⻢逊出品的一款基于机器学习的 AI 编程助手&#xff0c;可…

No210.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

软件测试/测试开发丨​利用人工智能ChatGPT批量生成测试数据

点此获取更多相关资料 简介 测试数据是指一组专注于为测试服务的数据&#xff0c;既可以作为功能的输入去验证输出&#xff0c;也可以去触发各类异常场景。 测试数据的设计尤为重要&#xff0c;等价类、边界值、正交法等测试用例设计方法都是为了更全面地设计对应的测试数据…

大数据Doris(二十二):数据查看导入

文章目录 数据查看导入 数据查看导入 Broker load 导入方式由于是异步的,所以用户必须将创建导入的 Label 记录,并且在查看导入命令中使用 Label 来查看导入结果。查看导入命令在所有导入方式中是通用的,具体语法可执行 HELP SHOW LOAD 查看。 show load order by create…

三大开源向量数据库大比拼

向量数据库具有一系列广泛的好处&#xff0c;特别是在生成式人工智能方面&#xff0c;更具体地说&#xff0c;是在大语言模型&#xff08;LLM&#xff09;方面。这些好处包括先进的索引和精确的相似度搜索&#xff0c;有助于交付强大的先进项目。 本文将对三种开源向量数据库&…

快照snapshot要点记录

目录 COW快照ROW快照 snapshot&#xff1a;快照 快照分为&#xff1a;COW快照、ROW快照 COW&#xff1a;Copy On Write 指写前复制技术 ROW&#xff1a;Redirect On Write 指写时重定向技术 COW快照 性能无法达到最高&#xff0c;因为每次都要与COW共享映射表进行比对。存储中…

opencv车牌识别<二>

目录 一、车牌识别算法流程 二、车牌检测 一、车牌识别算法流程 在解释ANPR代码之前&#xff0c;需要明白主要步骤和使用ANPR 算法的任务。ANPR 有两个主要步骤:车牌检测和车牌识别。车牌检测的目的是在整个视频帧中检测到车牌位置。当在图像中检测到车牌时&#xff0c;分割的…

【Java 进阶篇】JQuery 案例:全选全不选,为选择添彩

在前端的舞台上&#xff0c;用户交互是一场精彩的表演&#xff0c;而全选全不选的功能则是其中一段引人入胜的剧情。通过巧妙运用 JQuery&#xff0c;我们可以为用户提供便捷的全选和全不选操作&#xff0c;让页面更富交互性。本篇博客将深入探讨 JQuery 中全选全不选的实现原理…

WP光电信息学院2023年网络安全季度挑战赛-测试赛

签个到就跑WP Misc MISC-没爱了&#xff0c;下一个 下载附件压缩包解压之后&#xff0c;获得一个流量包文件 使用wireShark打开流量包&#xff0c;Ctrl F 搜索flag{即可获得flag flag{Good_b0y_W3ll_Done}MISC-送你一朵小花花 下载附件压缩包解压之后&#xff0c;获得一…

【Vue 本地项目运行https服务】

配置本地开发环境的https访问 1、下载证书生成库2、创建证书颁发机构3、创建证书4、创建成功后会有4个文件在我们项目根目录5、定位到ca.crt 文件所在在位置 双击 安装证书6、在vue.config.js中引入证书&#xff1b; 1、下载证书生成库 npm install -g mkcert2、创建证书颁发机…

理工ubuntu20.04电脑配置记录

8188gu无线网卡配置 首先下载github上的文件&#xff0c;进入文件夹 安装make命令 1. 查看usb无线网卡 sudo lsusb|grep 8188 2. 环境准备 sudo apt-get install git make build-essential git dkms linux-headers-$(uname -r) 3. 编译安装 git clone https://github.com…

C#中.NET 6.0 Windows窗体应用通过EF访问数据库并对数据库追加、删除记录

目录 一、应用程序设计 二、应用程序源码 三、生成效果 前文作者发布了在.NET 6.0 控制台应用中通过EF访问已有数据库&#xff0c;事实上&#xff0c;在.NET 6.0 Windows窗体应用中通过EF访问已有数据库也是一样的。操作方法基本一样&#xff0c;数据库EF模型和上下文都是自…

【概率论】Python:实现求联合分布函数 | 求边缘分布函数 | Joint distribution | Marginal distribution

猛戳订阅! 👉 《一起玩蛇》🐍 💭 写在前面:本章我们将通过 Python 手动实现联合分布函数和边缘分布函数,部署的测试代码放到文后了,运行所需环境 python version >= 3.6,numpy >= 1.15,nltk >= 3.4,tqdm >= 4.24.0,scikit-learn >= 0.22。 0x00 …

后端接口性能优化分析-问题发现问题定义

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&…

科技驱动固定资产管理变革:RFID技术的前沿应用

在当今激烈竞争的商业环境中&#xff0c;企业固定资产管理面临挑战&#xff0c;而RFID技术正以其独特特性和功能性彻底改变资产管理方式。本文将深入探讨RFID技术在固定资产管理中的革命性作用&#xff0c;并解析其应用带来的创新和便利。 RFID技术概述&#xff1a; RFID系统作…

网站业务使用高防CDN的好处是什么?

随着互联网的发展&#xff0c;网站业务在今天的数字化时代扮演着至关重要的角色。然而&#xff0c;随之而来的是各种网络安全威胁和攻击的增加&#xff0c;这给网站的稳定性和可靠性带来了挑战。为了应对这些威胁&#xff0c;高防CDN&#xff08;Content Delivery Network&…

LeetCode(14)加油站【数组/字符串】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 134. 加油站 1.题目 在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车&#xff0c;从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加…

GZ038 物联网应用开发赛题第6套

2023年全国职业院校技能大赛 高职组 物联网应用开发 任 务 书 &#xff08;第6套卷&#xff09; 工位号&#xff1a;______________ 第一部分 竞赛须知 一、竞赛要求 1、正确使用工具&#xff0c;操作安全规范&#xff1b; 2、竞赛过程中如有异议&#xff0c;可向现场考评…

Docker Desktop 开启失败 Unexcept WSL Error

Docker Desktop 开启失败 Unexcept WSL Error 原因 原因 安装了安卓模拟器&#xff0c;然后导致 WSL 起不来&#xff0c;尝试如下都没用 重置代理 —— netsh winsock resetBIOS 关闭、重启、再重新打开 CPU 虚拟化关闭 hyper-v、windows subsystem for linux 再重启 再开启卸…