java - 多线程 04 - 线程间的通信+源码解析

一、线程之间的通信

    1、 等待/唤醒机制;

        通过 wait 和 notify 来进行相关的通信(一定条件下的等待和唤醒)(例子:生产者和消费者)

        wait(会释放拿到的锁)、notify(随机叫醒一个处于 wait 状态的线程(同一锁对象情况下,先释放了 notify 的锁才能让唤醒的线程获取锁),此时此线程会再拿到锁)

        当线程在执行中 wait(); 之后,若要唤醒线程 ,则 需要在同步代码块中给同步线程run方法 调用notify唤醒

        使用条件:在同步代码块中使用,notify唤醒的是同一对象下的 wait 的线程;

 

    2、Condition 接口 -- await()、signal()  -- ConditionObject 实现类(juc下的AQS的内部类)

Lock lock = new ReentrantLock(); // 创建锁
Condition a = lock.newCondition(); // 创建 Condition,相当于创建了一个条件队列
Condition b = lock.newCondition();
// 在加锁的条件下使用
a.await(); //将指定 condition 所在的方法中的线程 wait
b.signal();//将指定 condition 所在的方法中的线程 notify

        2.1、condition 与 wait、notify(notifyAll)

                notify:随机唤醒一个同一对象下的线程; notifyAll:唤醒所有同一对象下的线程;

                condition.signal:唤醒指定 condition await 的线程

 

    3、join方法 -- 线程加塞

            在一个线程中执行另一个线程, thread.join(); // 线程加塞后可让加塞的线程执行完再执行原来线程接下来的代码

joinThread.start(); // 开启线程
joinThread.join(); // 阻塞当前线程,当 joinThread 线程执行完毕(销毁)则唤醒当前线程

// join 源码
public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
// -------------------------------------
    if (millis == 0) {
        // 调用 join 的线程在此自旋,循环将 当前线程 wait 0 milliseconds;当 joinThread 线程销毁后,则退出循环
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
// -------------------------------------
}

 

    4、ThreadLocal  -- 实现每一个线程都有自己的共享变量

            主要解决的就是每个线程绑定自己的值。将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的是有数据。不同线程中的值是可以放入ThreadLocal类中进行保存的

private ThreadLocal<Integer> count = new ThreadLocal<Integer>() {  // 创建了一个变量“盒子”,有get、set、remove等方法
   protected Integer initialValue() {  // 重写返回值方法
      return new Integer(0);
   };
};

            底层靠ThreadLocal的内部类ThreadLocalMap来实现不同线程间的资源存储。

 

    5、CountDownLatch并发工具类 --  一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

            - 用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待。

        -await的是当前线程

// 1、创建一个 CountDownLatch 的实例
CountDownLatch latch = new CountDownLatch(count);

// 2、在其他每个线程中调用一次  latch.countDown()方法 计数
latch.countDown();
// 3、在需要汇总操作前的位置 await 当前线程,当计数线程执行完销毁后,会自动唤醒当前线程
latch.await();

 

    6、CyclicBarrier并发工具类 --  一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。

        - CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。

        -await的是其他线程

// 在主线程中创建一个 CyclicBarrier 的实例;条件达到时会唤醒被await 的所有线程并执行 参数线程动作
CyclicBarrier barrier = new CyclicBarrier(10, new Runnable() {  // 参数为 (等待的线程数,达到等待数条件时执行的线程动作)
   @Override
   public void run() {
      // 执行动作
   }
});

//在其他线程中调用 await,每调用一次都会使线程等待数目条件 +1
barrier.await();

 

    7、Semaphore -  通常用于限制可以访问某些资源(物理或逻辑的)的线程数目

// 1、在主线程中创建一个 Semaphore 的实例,指定可执行的线程数作为参数
Semaphore semaphore = new Semaphore(10);

// 2、在其他线程调用的方法中
// 2.1、开始前用 semaphore 对象调用 acquire 方法
semaphore.acquire();
// 2.2、结束后用 semaphore 对象调用 release 方法
semaphore.release();

    - 当不断的有新的线程调用方法时,semaphore 便会阻塞限定数目外的线程,使这些线程进入等待状态

 

    8、Exchanger --  可以在对中对元素进行配对和交换的线程的同步点。每个线程将条目上的某个方法呈现给 exchange 方法,与伙伴线程进行匹配,并且在返回时接收其伙伴的对象。 Exchanger 可能在应用程序(比如遗传算法和管道设计)中很有用。

      -- 获取到另一个线程放入同一个Exchanger对象的 exchange 方法中的 信息

// 1、在主线程中创建一个 Exchanger 的实例
Exchanger<String> exch = new Exchanger<>();

// 2、在其他线程调用的方法中使用同一个 exch 实例来进行通信
String value = exch.exchange(res);  // 获取到另一个线程放入 exchange 方法中 res

 

二、Condition 源码解析

    1、创建一个 Condition 实例

        1.1、创建一个锁对象

Lock lock = new ReentrantLock(); // 创建锁

        1.2、使用锁创建一个 Condition 实例

Condition condition = lock.newCondition(); // 创建 Condition

    2、源码

        2.1、ReentrantLock 中的 newCondition 方法,调用 同步器中的 newCondition方法,创建一个 AQS 中的 ConditionObject 实例 返回

// ReentrantLock 下的 newCondition方法
public Condition newCondition() {
   return sync.newCondition();  // 调用 同步器中的 newCondition方法
}

// ReentrantLock 中内部类 Sync 的newCondition 方法
final AbstractQueuedSynchronizer.ConditionObject newCondition() {
   return new AbstractQueuedSynchronizer.ConditionObject();  // 创建一个 AQS 中的 ConditionObject 实例 返回
}

 

        2.2、 AQS 中的 ConditionObject内部类

            2.2.1、 await方法 - 将线程进入等待状态

           步骤:

            创建一个新的节点,将线程加入到条件队列(等待队列) -->  释放该线程获取的所有锁  -->  判断是否在同步队列中,不在同步队列中,则停止该线程  -->  若线程在同步队列中,则改变线程的状态,更新同步队列,然后停止该线程;

public final void await() throws InterruptedException {
   if (Thread.interrupted()) // 若线程被中断,则抛出异常
      throw new InterruptedException();
   AbstractQueuedSynchronizer.Node node = addConditionWaiter();  // 创建一个新的节点,将线程加入到条件队列(等待队列)
   int savedState = fullyRelease(node); // 释放该线程获取的所有锁
   int interruptMode = 0;
   while (!isOnSyncQueue(node)) { // 判断是否在同步队列中,不在同步队列中,则停止该线程
      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);
}

            2.2.2、signal方法 - 唤醒线程

           步骤:

            取出该condition条件队列中的头结点  -->  递归唤醒该 condition 下所有的 await的线程

public final void signal() {
   if (!isHeldExclusively()) // 是否为独占锁节点
      throw new IllegalMonitorStateException();
   AbstractQueuedSynchronizer.Node first = firstWaiter; // 取出该condition条件队列中的头结点
   if (first != null)
      doSignal(first);
}

private void doSignal(AbstractQueuedSynchronizer.Node first) {
   do {
      if ( (firstWaiter = first.nextWaiter) == null)
         lastWaiter = null;
      first.nextWaiter = null;
   } while (!transferForSignal(first) &&  // 唤醒该 condition 下所有的 await的线程
         (first = firstWaiter) != null); // 递归唤醒所有
}

    3、同步队列与条件队列(等待队列)

        对于同一个“AQS”来说,同步队列只有一个,而条件队列有多个,每个Condition实例都有一个自己的条件队列 

        同步队列正在排队等待获取锁的队列

        条件队列(等待队列):已经获取到锁,但被 await 后的线程所进入的队列;待队列被唤醒的时候,则会将队列中的线程加入到同步队列中取竞争锁

 

三、CountDownLatch、CyclicBarrier、Semaphore源码解析

    1、CountDownLatch 源码解析

  • 使用共享锁实现

  • 当其他全部线程未执行完毕调用 countDown 方法时,则主线程在调用await处处于等待状态

  • 解析构造方法、countDown方法、await方法

// 1、创建 CountDownLatch 实例
public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count); // 创建一个同步器实例
}
Sync(int count) {
    setState(count); // 设置state初始化的状态值 
}

// 2、在其他每个线程中调用一次  latch.countDown()方法 计数
// latch.countDown();
public void countDown() {
    sync.releaseShared(1); // 调用AQS的方法 释放一个锁,当 state 为 0 时,latch.await()的线程才能被唤醒
}

// 3、在需要汇总操作前的位置 await 当前线程,当计数线程执行完销毁后,会自动唤醒当前线程
// latch.await();
public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1); // 调用了AQS 的acquireSharedInterruptibly方法
}
// AQS
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())  // 查看当前线程是否为已中断状态
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)  // 调用了AQS的实现类CountDownLatch的 tryAcquireShared 方法
        doAcquireSharedInterruptibly(arg); // 中断当前线程
}
// CountDownLatch
protected int tryAcquireShared(int acquires) {
    return (getState() == 0) ? 1 : -1; // 若其他线程都执行完毕(调用了latch.countDown()),则返回 1,否则返回 -1,中断当前线程
}

 

 

    2、CyclicBarrier源码解析

  • 设置一个屏障点,使其他线程到达后调用 barrier.await(); 进行等待,直到指定条件达到时,由最后一个线程执行屏障点线程

  • 解析构造方法、await方法

// 1、在主线程中创建一个 CyclicBarrier 的实例;条件达到时会唤醒被await 的所有线程并执行 参数线程动作
/*CyclicBarrier barrier = new CyclicBarrier(10, new Runnable() {  // 参数为 (等待的线程数,达到等待数条件时执行的线程动作)
   @Override
   public void run() {
      // 执行动作
   }
});*/
public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties; 
    this.count = parties; // 记录需要等待的线程数
    this.barrierCommand = barrierAction;  // 记录目的线程
}

// 2、在其他线程中调用 await
// barrier.await();
public int await() throws InterruptedException, BrokenBarrierException {
    try {
        return dowait(false, 0L);  // 调用 dowait 来 await 线程,或在达到条件下唤醒所有线程并执行屏障点线程
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen
    }
}
// 核心方法 dowait
private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
        TimeoutException {
    final ReentrantLock lock = this.lock; // 获取锁,为了线程安全
    lock.lock();
    try {
        // 为提供reset方法设置,先不管 。。。
        final CyclicBarrier.Generation g = generation;
        if (g.broken)
            throw new BrokenBarrierException();
        if (Thread.interrupted()) { // 判断线程是否已被中断
            breakBarrier();
            throw new InterruptedException();
        }

        int index = --count; // 将所需达到条件线程数目 -1
        if (index == 0) {  // 若 index == 0,则表示已达到条件,可唤醒全部线程并执行屏障点线程
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand; // 获取屏障点线程
                if (command != null)
                    command.run(); // 执行 屏障点线程run方法
                ranAction = true;
                nextGeneration(); // 唤醒所有线程并重置generation
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier();
            }
        }

        // loop until tripped, broken, interrupted, or timed out
        //当不满足 屏障点条件时,则让当前线程await
        for (;;) {
            try {
                if (!timed)
                    trip.await(); // 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();
    }
}

 

    3、Semaphore源码解析

  • 限定指定数目的线程调用方法,其他线程只能被阻塞在外等待,类似一个限定了线程数目大小的共享锁,acquire获取锁、release释放锁

  • 使用了共享锁实现

  • 解析构造方法、acquire方法、release方法

// 1、创建一个 Semaphore 的实例;参数为可进入的线程数目
// Semaphore semaphore = new Semaphore(10);
public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}
NonfairSync(int permits) {
    super(permits); // 调用AQS的构造器方法,设置state 的初始状态值,也就可进入的线程数目
}
Sync(int permits) {
    setState(permits); 
}

// 2、调用 acquire 方法计数,每调用一次,state都会 -1
// semaphore.acquire();
public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg); // 当上判断返回值 < 0时,则说明此时进入的线程已达限定值,让接下来进入的线程进入同步队列等待
}
// tryAcquireShared 方法的调用,将状态值 -1,当可进入线程数目 > 0时,则在此自旋;否则返回-1
final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining)) 
            return remaining;
    }
}

// 3、调用 release方法计数,每调用一次,state都会 +1
public void release() {
    sync.releaseShared(1);
}
// 若线程释放成功,则唤醒同步队列的线程
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared(); // 若释放成功,则唤醒同步队列中的一个线程
        return true;
    }
    return false;
}
// 进行线程的释放,将状态值 +1
protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        int current = getState(); 
        int next = current + releases;
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
        if (compareAndSetState(current, next))
            return true;
    }
}

 

有啥不正确的还望大佬指正,谢谢

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值