线程控制工具类案例详解(带源码解析)

线程控制工具类:

       Condition  的使用及原理
           synchronized   与  wait /  notify   进行通信等待唤醒
           lock   与  Condition  与其有类似作用

           使用场景:生产者-消费者模型;有更强的控制性和扩展性


       CountDownLatch 的原理分析
           使用场景:倒计时计数器、模拟并发场景


       CyclicBarrier 原理分析
           使用场景:可重复使用的循环屏障;  类似:汽车站满员发班班车;


       Semaphore 的原理分析及与线程池的区别
           使用场景:限流

 

1、Condition  的使用及原理


案例: ConditionNotify  和 ConditionWait 实现一个notify 与wait的生产者-消费者效果;

    ConditionWait 

package com.hezm.thread.day1.tool;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class ConditionWait implements Runnable{
    private Lock lock;

    private Condition condition;

    public ConditionWait(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }


    @Override
    public void run() {
        lock.lock();   //加锁
        try {
            System.out.println("begin- ConditionWait");
            condition.await();    //阻塞
            System.out.println("end- conditionWait");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();    //释放锁
        }


    }
}
package com.hezm.thread.day1.tool;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class ConditionNotify implements Runnable{

    private Lock lock;

    private Condition condition;

    public ConditionNotify(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }

    @Override
    public void run() {
        try {
            lock.lock();   //加锁
            System.out.println("begin- conditionNotify");
            //唤醒一个等待在condition队列上的线程,将该线程转移到AQS同步队列中,
            //如果在同步队列中能够竞争到Lock则可以从ConditionWait#wait()方法中返回。
            condition.signal();    
            //ConditionNotify 中conditon  还要一个方法: 
            //signalAll(): 唤醒所有等待在condition队列上的线程
        }finally {
            System.out.println("end- conditionNotify");
            lock.unlock();    //释放锁
            System.out.println("unlock- conditionNotify");
        }
    }
}

     

ConditionNotify 

package com.hezm.thread.day1.tool;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class ConditionNotify implements Runnable{

    private Lock lock;

    private Condition condition;

    public ConditionNotify(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }

    @Override
    public void run() {
        try {
            lock.lock();   //加锁
            System.out.println("begin- conditionNotify");
            //唤醒一个等待在condition队列上的线程,将该线程转移到AQS同步队列中,
            //如果在同步队列中能够竞争到Lock则可以从ConditionWait#wait()方法中返回。
            condition.signal();    
            //ConditionNotify 中conditon  还要一个方法: 
            //signalAll(): 唤醒所有等待在condition队列上的线程
        }finally {
            System.out.println("end- conditionNotify");
            lock.unlock();    //释放锁
            System.out.println("unlock- conditionNotify");
        }
    }
}

 

  ConditionWait 中的wait方法

       1、void await() throws InterruptedException:当前线程进入等待状态,

             唤醒机制:如果其他线程调用condition的signal或者signalAll方法并且当前线程获取Lock从await方法返回,

             异常处理:如果在等待状态中被中断会抛出被中断异常;

       2、long awaitNanos(long nanosTimeout):当前线程进入等待状态

             唤醒机制:直到被通知,中断或者超时;  

             无异常处理;

       3、boolean await(long time, TimeUnit unit)throws InterruptedException:同第二种,支持自定义时间单位

       4、boolean awaitUntil(Date deadline) throws InterruptedException:当前线程进入等待状态

             唤醒机制:直到被通知,或者到了某个时间

             异常处理:如果在等待状态中被中断会抛出被中断异常;

主运行程序:

package com.hezm.thread.day1.tool;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class ConditionMain {


    public static void main(String[] args) {

        //  类似一个生产者消费者模型
        Lock lock = new ReentrantLock();  //需要是一个重入锁;
        Condition condition = lock.newCondition();

        //两个线程必须是同一把锁和condition  ,
        new Thread( new ConditionWait(lock, condition)).start();
        new Thread(new ConditionNotify(lock,condition)).start();
    }
}

运行结果:

begin- ConditionWait
begin- conditionNotify
end- conditionNotify
end- conditionWait
unlock- conditionNotify

或者

begin- ConditionWait
begin- conditionNotify
end- conditionNotify
unlock- conditionNotify
end- conditionWait

结果解析

     这里似乎是在 ConditionNotify 执行了lock.unlock()才真正的释放锁;  否则为何一直执行都没有end- conditionNotify 在 end- conditionWait 之后执行的呢?

 

源码分析:

     a,关键数据结构

       condition等待队列: 是通过AQS#Node来存储等待状态waitStatus和线程Thread;通过nextWaiter维护下一个Node实现的单向队列

       ConditionObject是AQS的一个内部类,因此每个ConditionObject能够访问到AQS提供的方法,相当于每个Condition都拥有所属同步器的引用

      当我们多次调用lock.newCondition()的时候就创建了多个ConditionObject对象;具体结构如下:

       AQS同步队列:taile和head 的AQS#Node存储节点状态waitStatus 和线程Thread,通过prev和next实现的双向队列;

          其核心是状态管理; 实现了独占锁(acquire(int arg)、acquireInterruptibly(int arg) -可中断、tryAcquireNanos(int arg, long nanosTimeout) - 超时、 release(int arg) )、共享锁(acquireShared(int arg)、acquireSharedInterruptibly(int arg)-可中断、tryAcquireSharedNanos(int arg, long nanosTimeout)-超时、 releaseShared(int arg)  )、可中断锁、超时等待锁等特性

          AQS同步锁的源码分析

          waitStatus 核心状态值

int CANCELLED = 1//节点从同步队列中取消
int SIGNAL = -1//后继节点的线程处于等待状态,如果当前节点释放同步状态会通知后继节点,使得后继节点的线程能够运行;
int CONDITION = -2//当前节点进入等待队列中
int PROPAGATE = -3//表示下一次共享式同步状态获取将会无条件传播下去
int INITIAL = 0;//初始状态

     b,Contion底层执行过程

    第一步ConditionWait先运行lock.lock();
        获取到锁,Thread2会加入AQS队列,构建双向的头尾链表等待队列(null 为 head头   Thread2为尾 tail );
    第二步执行 condition.await();AQS.await()
         1,Thread1阻塞,加入Contion队列 :构建fistWaiters、 nextWaiters、 lastWaiters  三个节点指向的单向链表同步队列
              新构建一个Node节点封装Thread1插入这个单向链表
         2,fullyRelease()  执行释放锁  ,因为是重入锁,所以需要加fully进行全部释放;重入多少次就释放多少次;这里Thread2就会去抢占锁
         3,!isOnSyncQueue(node)   当node不在AQS队列的时候将  Thread1 阻塞 LockSupport.park(this);
             //  当先中断后 lock.signal() 的时候抛出异常,   先执行lock.signal() 再执行 interrupt() 重新发起中断;
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                 break;
    第三步执行 ConditionNotify 执行lock.lock();
         获取到锁,AQS队列的Thread2  进行执行状态,并且在AQS队列中成为 head头  同时后面没有新的Thread插入队列 tail也是Thread2
    第四步 执行  lock.signal()  ;调用 doSignal(fistWaiter)
         获取 Condition 的 fistWaiters ,  transferForSignal(Node node)中 enq(node) 方法将 fistWaiters 添加到AQS队列中,返回 fistWaiters;
         然后将 fistWaiters 从condition队列中移除掉;
         LockSupport.unpark(node.thread); 唤醒Thread1;
         {其他唤醒的时候:Thread.interrupt() 也会对线程进行唤醒    }
    第五步  ConditionNotify 执行了lock.unlock(),真正的释放锁,公平锁唤醒head的下一个节点Thread1 将拿到锁继续执行;
         非公平锁的时候用CAS方式争抢锁;

 

//第一步:lock.lock()  ,最终调用:AbstractQueuedSynchronizer # acquire() 
//   简单描述:抢占锁成功,执行当前线程,并初始化AQS队列同步器;
//   内部详细:参考  https://mp.csdn.net/postedit/98208145
public final void acquire(int arg) {
    //
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}


//第二步:condition.await()   将该线程阻塞,并从AQS同步队列的头节点移除,尾插加入ContionObject等待队列;
        public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();  //@1
            int savedState = fullyRelease(node);   //@2
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {    //@3
                LockSupport.park(this);   /**阻塞:直至被signal/signalAll唤醒;*/
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)  //@4
                    break;
            }
            // 自旋等待获取到同步状态(即获取到lock锁)
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // 被取消则清除
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

//addConditionWaiter();  @1   构建加入Contion队列的Node:构建fistWaiters、
//lastWaiters均指向Node{Thread1, nextWaiters = null} 的单向链表等待队列
//              新构建一个Node节点封装Thread1插入这个单向链表

//fullyRelease(node)   @2  执行释放锁,因为是重入锁,所以需要加fully进行全部释放;重入多少次就释放多少次;
//       调用AQS的模板方法release方法释放AQS的同步状态并且唤醒在同步队列中头结点的后继节点引用的线程

//!isOnSyncQueue(node)    //@3 当node不在AQS队列的时候调用LockSupport.park(this) 
// 将Thread1 阻塞 ,在AQS队列则跳出循环;

//(interruptMode = checkInterruptWhileWaiting(node)) != 0    //@4  当程序中断则跳出循环
//  会去执行reportInterruptAfterWait(interruptMode);
//     情况1:interruptMode = -1 当先中断后 lock.signal() 的时候抛出异常,   
//     情况2:interruptMode = 1 先执行lock.signal() 再执行 interrupt() 调用selfInterrupt();重新发起中断;
//        重新发起中断
           


//第三步执行 ConditionNotify 执行lock.lock();
//         ConditionNotify线程获取到锁,AQS队列的Thread2进入执行状态,并且在AQS队列中成为 head头  同时后面没有新的Thread插入队列 tail也是Thread2

//第四步 执行  lock.signal()  ;调用 doSignal(fistWaiter)
        private void doSignal(Node first) {  //@1
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;   //@2
            } while (!transferForSignal(first) &&      //@3
                     (first = firstWaiter) != null);   // @4
        }

//Node first @1在singal()中获取的Condition等待队列的fistWaiters 
// first.nextWaiter = null;   //@2  将等待队列的头节点从等待队列中移除
// transferForSignal(Node node)   //@3中 enq(node) 方法将头结点添加到AQS队列中,添加成功返回true;
// (first = firstWaiter) != null);   // @4 等待队列下一个线程不为null则继续循环,直到为null

    final boolean transferForSignal(Node node) {
        //将Node的节点状态设置为初始化INIT
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        //将头结点Node加入到AQS队列中,
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);   //当线程被取消或者修改线程状态为SIGNAL失败唤醒线程
        return true;
    }


//第五步  ConditionNotify 执行了lock.unlock(),真正的释放锁,公平锁唤醒head的下一个节点非公平锁的时候用CAS方式争抢锁;
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);   //唤醒AQS同步队列head.next的Node中的线程
            return true;
        }
        return false;
    }


源码参考

 

 


  2、CountDownLatch 的原理分析

案例: CountDownLatchDemo 的两个案例

对应场景:模拟并发、控制等待前面线程任务执行完成再继续;

package com.hezm.thread.day1.tool;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo1 extends Thread{
    //场景模拟并发场景
    static CountDownLatch countDownLatch = new CountDownLatch(1);

    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            new CountDownLatchDemo().start();
        }
        countDownLatch.countDown();
    }
    @Override
    public void run() {
        try {
            countDownLatch.await();  //阻塞    共享锁用的 Thread.currentThread() 阻塞
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("ThreadName:" + Thread.currentThread().getName());
    }
}




//场景2:并发控制,当前面程序执行完成之后再执行之后的逻辑
public class CountDownLatchDemo2 extends Thread{

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch downLatch = new CountDownLatch(3);
        new Thread(()->{
            System.out.println("Thread1");
            downLatch.countDown();
        });

        new Thread(()->{
            System.out.println("Thread2");
            downLatch.countDown();
        });

        new Thread(()->{
            System.out.println("Thread3");
            downLatch.countDown();
        });
        downLatch.await();
    }
}

原理分析:

利用了AQS的可中断共享锁;实现多个线程在同一时间等待;

       new CountDownLatch(1);中将数值设置给  state 作为计数器;  

       判断当前计数器 == 0 则表示可以被释放和唤醒。

 

核心方法

  • await() throws InterruptedException:调用该方法的线程等到构造方法传入的N减到0的时候,才能继续往下执行;
  • await(long timeout, TimeUnit unit):与上面的await方法功能一致,只不过这里有了时间限制,调用该方法的线程等到指定的timeout时间后,不管N是否减至为0,都会继续往下执行;
  • countDown():使CountDownLatch初始值N减1;
  • long getCount():获取当前CountDownLatch维护的值;

 

源码分析:

   /**
     * 第一步:new CountDownLatch(1);中将数值设置给  state作为计数器;  判断当前计数器 == 0 则表示可以被释放和唤醒。
     */    
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

    protected final void setState(int newState) {
        state = newState;  //设置线程状态值为  count中的数值;
    }


   //第二步:countDownLatch.await() 阻塞线程:
     private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);   // @1构建共享锁AQS同步队列 - Node.SHARED
        boolean failed = true;
        try {
            for (;;) {    //自旋
                final Node p = node.predecessor();   //@2
                if (p == head) {                     //@3 拿到当前的节点的前一个节点
                    int r = tryAcquireShared(arg);   // 尝试竞争这个锁,计数器不为0 返回-1,0返回1;
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);   //唤醒所有线程
                        p.next = null; // 清除AQS同步队列所有子节点   
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&   // 则阻塞队列所有线程
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    

//第三步:countDown()

     public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {  //获取状态值是否修改为0
            doReleaseShared();    //释放共享锁
            return true;
        }
        return false;
    }


       protected boolean tryReleaseShared(int releases) {
            //  自旋获取计算器为0的时候修改状态值
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
   

 

    3、 CyclicBarrier 原理分析

使用案例:

使用场景:导入数据,当达到多少个导入进行一个批次的处理;  批量循环使用的线程控制工具类;

package com.hezm.thread.day1.tool;


import java.util.concurrent.CyclicBarrier;

/***
 * 实现案例:   在不同的地方获取数据,在三个地方获取数据,都获取到之后才能进行计算;
 */
public class CycliBarrierDemo extends Thread{

    @Override
    public void run() {
        System.out.println("开始进行数据分析");
    }

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new CycliBarrierDemo());
        new Thread(new ImportDataThread(cyclicBarrier,"path1")).start();
        new Thread(new ImportDataThread(cyclicBarrier,"path2")).start();
        new Thread(new ImportDataThread(cyclicBarrier,"path3")).start();
        //以上完成才可以进行数据分析, 调用  CycliBarrierDemo.start();
    }
}



package com.hezm.thread.day1.tool;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class ImportDataThread  extends Thread {
    private CyclicBarrier cyclicBarrier;
    private String path;

    public ImportDataThread(CyclicBarrier cyclicBarrier, String path) {
        this.cyclicBarrier = cyclicBarrier;
        this.path = path;
    }

    @Override
    public void run() {
        System.out.println("开始导入"+path+"路径的数据");
        //todo
        try {
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}

运行结果

      开始导入path2路径的数据
      开始导入path3路径的数据
      开始导入path1路径的数据
      开始进行数据分析

 

核心方法:

//等到所有的线程都到达指定的临界点
await() throws InterruptedException, BrokenBarrierException 

//与上面的await方法功能基本一致,只不过这里有超时限制,阻塞等待直至到达超时时间为止
await(long timeout, TimeUnit unit) throws InterruptedException, 
BrokenBarrierException, TimeoutException 

//获取当前有多少个线程阻塞等待在临界点上
int getNumberWaiting()

//用于查询阻塞等待的线程是否被中断
boolean isBroken()


//将屏障重置为初始状态。如果当前有线程正在临界点等待的话,将抛出BrokenBarrierException。
void reset()

 

源码分析:

//第一步: new CyclicBarrier(3, new CycliBarrierDemo())

    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;   //记录线程数
        this.barrierCommand = barrierAction;   //在栅栏执行线程数完成的时候执行该action的 run()方法
    }


//第二步: new Thread(new ImportDataThread(cyclicBarrier,"path1")).start();  将锁要执行的线程包裹进新创建的线程池中;


//第三步:cyclicBarrier.await();   将线程阻塞在这里,等待唤醒
    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();

            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }

            int index = --count;      //统计数-1
            if (index == 0) {  // tripped
                boolean ranAction = false;
                try {
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();     //执行CycliBarrierDemo的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 {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        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() {
        // signal completion of last generation
        trip.signalAll();
        // set up next generation
        count = parties;
        generation = new Generation();
    }

 

 

 

  4、Semaphore 

使用案例:通过Semaphore实现小车的限流驶入和驶出

适用场景:限流,比如网关限制api并发访问量、数据库限制并发访问数

package com.hezm.thread.day1.tool;

import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
   
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(5);
        for (int i = 0; i < 10; i++) {
            new Car(i+1, semaphore).start();
        }
    }


    //定义车
    static class Car extends Thread{
        private int num;
        private Semaphore semaphore;

        public Car(int num, Semaphore semaphore) {
            super();
            this.num = num;
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();   //获得一个令牌,拿不到令牌,就会阻塞;
                System.out.println("小车" + num + "抢占一个车位");
                Thread.sleep(1000);
                System.out.println("小车" + num + "开走了");
                /*** 释放锁 */
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

核心方法:

//获取许可,如果无法获取到,则阻塞等待直至能够获取为止
void acquire() throws InterruptedException 

//同acquire方法功能基本一样,只不过该方法可以一次获取多个许可
void acquire(int permits) throws InterruptedException

//释放许可
void release()

//释放指定个数的许可
void release(int permits)

//尝试获取许可,如果能够获取成功则立即返回true,否则,则返回false
boolean tryAcquire()

//与tryAcquire方法一致,只不过这里可以指定获取多个许可
boolean tryAcquire(int permits)

//尝试获取许可,如果能够立即获取到或者在指定时间内能够获取到,则返回true,否则返回false
boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException

//与上一个方法一致,只不过这里能够获取多个许可
boolean tryAcquire(int permits, long timeout, TimeUnit unit)

//返回当前可用的许可证个数
int availablePermits()

//返回正在等待获取许可证的线程数
int getQueueLength()

//是否有线程正在等待获取许可证
boolean hasQueuedThreads()

//获取所有正在等待许可的线程集合
Collection<Thread> getQueuedThreads()

 

原理分析:

 permits:令牌,5个令牌数则只能同时运行5个线程,当有的完成之后再可以处理排队的任务

锁竞争类型:有公平锁 和 非公平锁;

public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

   

源码解析:

//第一步:new Semaphore(5); 初始化公平锁/非公平锁  设定令牌数量
    protected final void setState(int newState) {
        state = newState;   //设定令牌数
    } 


//第二步:semaphore.acquire(); 获得令牌,获取不到令牌就会阻塞等待
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)     //要获取的令牌数 arg还够不够,  < 0
            doAcquireSharedInterruptibly(arg);    //进入AQS的共享锁锁定
    }

    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);   // @1构建共享锁AQS同步队列 - Node.SHARED
        boolean failed = true;
        try {
            for (;;) {    //自旋
                final Node p = node.predecessor();   //@2
                if (p == head) {                     //@3 拿到当前的节点的前一个节点
                    int r = tryAcquireShared(arg);   // 尝试竞争这个锁,计数器不为0 返回-1,0返回1;
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);   //唤醒所有线程
                        p.next = null; // 清除AQS同步队列所有子节点   
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&   // 则阻塞队列所有线程
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }


//第三步: semaphore.release(); 释放令牌,唤醒阻塞等待的任务  可参考上面CountDownLatch 的案例


 


Seamphore和线程池的区别:

1、实际工作的线程是谁创建的?

        使用线程池,实际工作线程由线程池创建;

        使用Seamphore,实际工作的线程由你自己创建。

2、限流是否自动实现?     

        线程池自动;

        Seamphore手动。

 

 

4、Exchanger

使用案例:通过Exchanger实现情书传送

适用场景:适用于线程间交换数据

public class ExchangerDemo {
    private static Exchanger<String> exchanger = new Exchanger();

    public static void main(String[] args) {

        //代表男生和女生
        ExecutorService service = Executors.newFixedThreadPool(2);

        service.execute(() -> {
            try {
                //男生对女生说的话
                String girl = exchanger.exchange("我其实暗恋你很久了......");
                System.out.println("女孩儿说:" + girl);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        service.execute(() -> {
            try {
                System.out.println("女生慢慢的从教室你走出来......");
                TimeUnit.SECONDS.sleep(3);
                //男生对女生说的话
                String boy = exchanger.exchange("我也很喜欢你......");
                System.out.println("男孩儿说:" + boy);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

    }
}

 

运行结果

女生慢慢的从教室你走出来......
男孩儿说:我其实暗恋你很久了......
女孩儿说:我也很喜欢你......

 

主要方法:

//当一个线程执行该方法的时候,会等待另一个线程也执行该方法,因此两个线程就都达到了同步点
//将数据交换给另一个线程,同时返回获取的数据
V exchange(V x) throws InterruptedException

//同上一个方法功能基本一样,只不过这个方法同步等待的时候,增加了超时时间
V exchange(V x, long timeout, TimeUnit unit)
    throws InterruptedException, TimeoutException 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值