JAVA多线程的初级认识6-Condtion以及一些同步工具的使用

7 篇文章 0 订阅

Condition

1.5诞生Condition接口,源码如下:

public interface Condition {
	void await() throws InterruptedException;
	void awaitUninterruptibly();
	long awaitNanos(long nanosTimeout) throws InterruptedException;
	boolean await(long time, TimeUnit unit) throws InterruptedException;
	boolean awaitUntil(Date deadline) throws InterruptedException;
	void signal();
	void signalAll();
}

核心两个方法:await和signal.
其实类似于Object对象中的wait和notify。类比synchronized和Lock,可以看出JUC对于原有的方法的修改和提升。具体实现细节,如下慢慢分析。

AQS的实现

基本其他的JUC的同步共享锁实现类或多或少的调用了该实现,所以直接分析AQS实现类。

基本属性

	/** First node of condition queue. */
	private transient Node firstWaiter;
	/** Last node of condition queue. */
	private transient Node lastWaiter;

从源码里面可以看出,condition是实现了一个单向链表(仅仅维护nextWaiter)。

await方法

先看源码:

        public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            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);
        }

时序图~

1561020109016

  • AddConditionWaiter

将当前Node放入到Condition单向链表当中,初始化firstWaiter和lastWaiter

  • fullyRelease

释放当前锁,因为可能是重入,所以要release(state)并且保存state

  • isOnSyncQueue

循环判断是否是在同步队列,如果不是,则park这个线程

  • acquireQueued

当park的线程被唤醒,也就是他已经在同步队列中了。随后立刻尝试获取锁,同时获取state的重入。(当然也可能获取不到,排在同步队列当中)

随后是清理Cancel状态的节点,同时如果有异常打印异常。

signal方法

AQS源码:

        public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }
  • isHeldExeclusively方法

看看是不是自己唤醒自己…

  • doSignal方法

按照顺序从firstWaiter开始找到第一个Condition状态的节点,更新状态并且放入到同步队列单中。

signalAll方法

AQS源码:

        public final void signalAll() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignalAll(first);
        }
  • isHeldExeclusively方法

看看是不是自己唤醒自己…

  • doSignalAll方法

按照顺序从firstWaiter开始更新状态并且放入到同步队列单中。

总结

  • JUC的await和signal类似于Object类中的wait和notify。个人感觉只是wait和notify是native的,是在cpp代码里面实现,而await和signal是在java代码里面实现,并且提供更多的功能。

  • 每一个lock都可以有多个condition,同时也就拥有了多个单向链表

        public Condition newCondition() {
            return sync.newCondition();
        }
    
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
    

JUC的同步工具

简单介绍下CyclicBarrier, CountDownLatch, Semaphore。

CountDownLatch

内部维护了的sync类实现了AQS的共享锁,重新复写了获取和释放共享锁的方法。源码如下:

   /**
     * Synchronization control For CountDownLatch.
     * Uses AQS state to represent count.
     */
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }
  • 共享锁不像独享锁,仅仅关心state是否为0.
  • 初始化时候设置state,每次countDown都state都-1.

用法Demo

public class CountDownLatchDemo {

    /*
    发令枪,所有都准备好,发令枪一开始全部运行
     */
    private static CountDownLatch countDownLatch = new CountDownLatch(1);

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for(int i=0;i<10;i++){
            executorService.execute(()->{
                try {
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "\t finish!");
            });
        }
        System.out.println("Main Thread finish!");
        TimeUnit.SECONDS.sleep(1);
        countDownLatch.countDown();
        executorService.shutdown();
    }
}
public class CountDownLatchDemo2 {

    /*
    计数器,当前面任务完成了~在执行后面任务
     */
    private static CountDownLatch countDownLatch = new CountDownLatch(10);

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for(int i=0;i<10;i++){
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName() + "\t finish!");
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        System.out.println("Main Thread finish!");
        executorService.shutdown();
    }
}

CyclicBarrier

类似于CountDownLatch,当屏障经历过后~又会有一个新的一样的屏障。

  • 实现方法 没有用到AQS(可能是因为比较简单把…)直接用Lock和Condition实现。
  • 核心方法await 源码如下:
    /**
     * Main barrier code, covering the various policies.
     */
    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;
            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 {
                        // 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();
        }
    }

代码流程

  • 锁住资源,然后进行状态和参数校验
  • 减少数量,如果减少没有到0,则await。
  • 如果减少到0了,则执行runnable,重新更新generation。Runnable仅仅执行run方法,而非开新线程

用法demo

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

public class CycleBarrierDemo {

    public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
        int travellers = 3;
        CyclicBarrier guide = new CyclicBarrier(travellers, new Guide());
        for (int i = 1; i < 4; i++) {
            System.out.println(String.format("************第%d天****************", i));
            new Thread(new Travller("eric", guide,1000L)).start();
            new Thread(new Travller("robin", guide,2000L)).start();
            new Thread(new Travller("edward", guide,3000L)).start();
            Thread.sleep(4000L);
        }
    }

    static class Guide implements Runnable{

        @Override
        public void run() {
            System.out.println("collect passport!!!");
        }
    }

    static class Travller implements Runnable{
        private String name;
        private CyclicBarrier guide;
        private long lateTime;

        Travller(String name, CyclicBarrier guide, long lateTime){
            this.name = name;
            this.guide = guide;
            this.lateTime = lateTime;
        }

        @Override
        public void run() {
            try{
                System.out.println(name + ":\t late:" + lateTime);
                Thread.sleep(lateTime);
                guide.await();
                System.out.println(name + "\t begin to travel!");
            }catch (Exception e){

            }
        }
    }
}

三个游客,一个导游,三天。

  • 一个游客一个线程,迟到的时间不同,被导游收集完护照可以旅行
  • 一个导游负责游客都倒了,收集护照
  • 三天用来表现导游可以被复用(new Generation)

Semaphore

中文翻译:信号。

内部私有静态类Sync实现了AQS,同时内部也实现了公平锁和非公平锁。

公平锁和非公平锁区别:

  • 对于tryAccquireShared方法的实现不同

    • 公平锁 通过CAS循环来减少state,但是每次循环之前先去判断是否在同步Queue里面有前辈。如果有则返回。源码:

          static final class FairSync extends Sync {
              private static final long serialVersionUID = 2014338818796000944L;
      
              FairSync(int permits) {
                  super(permits);
              }
      
              protected int tryAcquireShared(int acquires) {
                  for (;;) {
                      if (hasQueuedPredecessors())
                          return -1;
                      int available = getState();
                      int remaining = available - acquires;
                      if (remaining < 0 ||
                          compareAndSetState(available, remaining))
                          return remaining;
                  }
              }
          }
      
    • 非公平锁 通过CAS循环来减少state。源码:

              final int nonfairTryAcquireShared(int acquires) {
                  for (;;) {
                      int available = getState();
                      int remaining = available - acquires;
                      if (remaining < 0 ||
                          compareAndSetState(available, remaining))
                          return remaining;
                  }
              }
      

用法Demo

可以理解为停车场一共就有那么多个车位(),只有车走了,才能有车在停进来,如果没有的话,就一直hang在那里

package juc;

import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {

    static Random random = new Random();

    public static void main(String[] args) {
        Semaphore parkingLot = new Semaphore(3);

        for (int i = 0; i < 10; i++) {
            new Thread(new ParkingCar(parkingLot, ("car"+i))).start();
        }

    }

    private static class ParkingCar implements Runnable{

        private Semaphore parkingLot;
        private String carName;

        ParkingCar(Semaphore parkingLot, String carName){
            this.parkingLot = parkingLot;
            this.carName = carName;
        }

        @Override
        public void run() {
            try {
                //等待停车场有车位,有则直接进,没有则hang住,进入同步队列等待,直到有车走了(释放了同步锁)
                //公平锁和非公平锁区别就在这里,如果有车提前排队了,那么公平锁就会让前面的车先进去,非公平锁就是同时争抢,谁先cas成功算谁的
                parkingLot.acquire();
                System.out.println(carName + "\tis entering!!!");
                int parkTime = random.nextInt(5);
                TimeUnit.SECONDS.sleep(parkTime);
                System.out.println(String.format("%s\tis leaving after parking %d second******", carName, parkTime));
                parkingLot.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

个人笔记。如有错误还请帮忙指出,多谢~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值