IO流&锁&CAS类&线程协作相关类

312 阅读5分钟

IO流

BIO是阻塞io

在IO连接的建立期间,线程进行了挂起。产生了资源浪费。

程序在发起io请求的时候,程序线程挂起,内核进入等待数据,数据等待之后,需要将数据拷贝到程序中,线程苏醒。

NIO异步IO

线程不会挂起,而是通过select进行轮训多个channel,将channel分为,tcp连接建立,tcp数据传输,数据准备完成,tcp断开连接。这样将会避免线程挂起。

AIO

NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 但是java对于aio支持的不是很好。

AtomicReference:原子性对象引用,提供了引用变量的读写原子性操作。

例如两个对象调用同一个person实例,进行操作就会产生问题,例如int 的++操作。可以通过 new AtomicReference<Person+>(person);进行原子性的getAndSet方法

Lock & ReentrantLock

java.util.concurrent.locks.Lock 方法中tryLock(time,unit)等待获得锁 java.util.concurrent.locks.ReentrantLock 构造函数中会传入bool值,根据这个bool值会影响是否加公平锁,公平锁加上之后,会进入队列,不加公平锁会先抢占资源,然后进入队列。 ReentrantLock 可重入锁,当一个线程请求得到一个对象锁后再次请求此对象锁,可以再次得到该对象锁,就是说在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以拿到锁.

CLH思想:

  • tail 元素,一个链表末尾的元素,它的getAndSet方法是原子性操作。因为是使用AtomicReference包装。
  • 每一个线程拥有 Pred节点和Node节点,Pred节点指的是当前线程的队列之前的线程,node节点指的是当前线程

详解如下:www.cnblogs.com/yuyutianxia…

ReentrantLock 锁的获取顺序:

  • 非公平模式(默认) - 主张竞争获取,可能会延缓一个或多个读或写线程,但是会比公平锁有更高的吞吐量 - 非公平获取锁详情如下:
  • 公平模式 - 线程将会以队列的顺序获取锁。当当前线程释放锁后,等待时间最长的写锁线程就会被分配写锁;或者有一组读线程组等待时间比写线程长,那么这组读线程组将会被分配读锁。 - 公平获取锁详情如下:

ReentrantLock 是如果获取的:

  • 获取锁的详情:

其中没有获取到锁,将会挂起线程。 在没有特殊需要的时候,可以用sychnoized

CAS 是什么?

Compare and Set/Swap AtomicXXX都是getSet原子性操作。

多线程协调(这里面的3个类都会造成线程阻塞)

Semaphore - permits count

对于需要控制总的同时访问个数的时候,可以使用这个类,但是这个类不能动态修改permit个数,如果需要修改,要继承该类。实现reducePermits方法,可以减少,多调用几次release方法,可以增加令牌个数。

CountDownLatch - one wait for all.

场景:在子线程中countDown()的时候,在main线程中调用await()方法,会挂起main线程直到所有调用countDown()方法的线程结束,main线程才唤醒。

CyclicBarrier - all wait for all

当所有子线程都调用了await()方法之后,所有子线程才会被唤醒。

Guava多线程

ListenableFuture

可以通过MoreExecutors.listeningDecorator(线程池)。当提交一个job的时候,将会返回一个ListenableFuture对象,可以通过addListener()添加当前子线程执行完成之后的后置处理。

Futures

常用方法: sucessfulAsList方法,将多个ListenableFuture转化成一个ListenableFuture,再对其添加addListener方法,这样就可以达到当所有子线程执行完成的后置处理了。 transformAsync方法,ListenableFuture取出结果并转换。 transform方法只是用来做转换的 ,将一种ListenableFuture转化成另外一种 addCallback可以处理成功和失败两种情况,一般用于最后的异常捕获。

CompletableFuture 构建异步应用

出现原因:

  • 将两个异步计算合并为一个——这两个异步计算之间相互独立,同时第二个又依赖于第一个的结果。
  • 等待 Future 集合中的所有任务都完成。
  • 仅等待 Future集合中最快结束的任务完成(有可能因为它们试图通过不同的方式计算同一个值),并返回它的结果。
  • 通过编程方式完成一个Future任务的执行(即以手工设定异步操作结果的方式)。
  • 应对 Future 的完成事件(即当 Future 的完成事件发生时会收到通知,并能使用 Future 计算的结果进行下一步的操作,不只是简单地阻塞等待操作的结果)

CompletableFuture实现了CompletionStage接口的如下策略:来源

  • 为了完成当前的CompletableFuture接口或者其他完成方法的回调函数的线程,提供了非异步的完成操作。
  • 没有显式入参Executor的所有async方法都使用ForkJoinPool.commonPool()为了简化监视、调试和跟踪,所有生成的异步任务都是标记接口AsynchronousCompletionTask的实例。
  • 所有的CompletionStage方法都是独立于其他共有方法实现的,因此一个方法的行为不会受到子类中其他方法的覆盖。
  • supplyAsync用于有返回值的任务,runAsync则用于没有返回值的任务。Executor参数可以手动指定线程池,否则默认ForkJoinPool.commonPool()系统级公共线程池
  • 注意:这些线程都是Daemon线程,主线程结束Daemon线程不结束,只有JVM关闭时,生命周期终止。
  • allOf是等待所有任务完成,构造后CompletableFuture完成
  • anyOf是只要有一个任务完成,构造后CompletableFuture就完成
  • 以Async结尾的方法都是可以异步执行的,如果指定了线程池,会在指定的线程池中执行,如果没有指定,默认会在ForkJoinPool.commonPool()中执行
  • thenAcceptAsync 异步消耗掉流
  • thenCombineAsync 结合两个CompletionStage的结果,进行转化后返回。
  • thenAcceptBothAsync 消耗两个流。
  • acceptEither 承受另一个。
  • handle 对于异常和正常的结果进行处理。