欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JUC(java.util.concurrent)要点笔记

程序员文章站 2022-04-10 22:45:56
...
    • iteye没落了,编辑格式太难整,表格位置有问题,图片无法上传
  • 线程

    线程6种状态 java.lang.Thread.State:
  • NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED

启动线程的3种方式:Thread类、Runnable接口、Executor线程池(实际工作中推荐使用这种方式:see《阿里巴巴Java开发手册(终极版)》一、编程规约-(六)并发处理-条目3

 

1个Java进程至少2个线程:main、GC

 

wait 和 sleep区别(JUC版sleep: 用TimeUnit类,例如TimeUnit.SECONDS.sleep(2)):

  wait sleep
调用者 Object对象 Thread类静态调用
释放锁 Y N
使用范围 与notify成组使用、用于线程通信 单独使用、哪里都可以用
异常捕获 可以不捕获 需捕获

 

 

  • synchronized 和 Lock 区别:

  synchronized java.util.concurrent.locks.Lock
语法 关键字 接口
尝试获取锁 N、若A获得则B一直等待 Y、可尝试获取若失败则放弃
自动释放锁 Y N
公平锁 N 默认非公平、可设置
精准控制 N、适合代码量小的同步 Y
  • synchronized锁的是谁?static synchronized 或 synchronized(xxx.class) 锁的是class模板对象、否则锁的是调用者。

  • 线程编码口诀:线程操作资源类

  • Callable接口与Runnable区别

         
      synchronized Runnable
    返回值 Y N
    声明抛出异常 Y
    注:当线程池中Callable任务运行异常时、异常会被抛到外部、外部可通过ExecutionException捕捉到
    N
    注:当线程池中Runnable任务运行异常时、异常不会被抛到外部、需在run方法中处理

Future接口

   
方法 含义
get() 获取结果(阻塞)
get(long timeout, TimeUnit unit) 在指定时间内获取结果(阻塞)
isDone() 任务是否已完成(非阻塞)
cancel(boolean mayInterruptIfRunning) 取消任务,并返回命令是否发送成功
注:只要发送成功就返回true,和任务是否确实被取消无关
isCancelled() 任务是否已被取消
注:
1.当使用cancel(false)时,isCancelled()=true也不表示任务已中断;
2.当使用cancel(true)时,isCancelled()=true也不表示任务已中断,是否中断成功依赖于 if (Thread.currentThread().isInterrupted()) 代码块

 

线程通信

 

  • 线程之间通信:判断、执行、通知

  • 虚假唤醒问题:使用while进行条件判断

  • 生产者/消费者问题synchronized和JUC版实现对比:

  synchronized JUC版
判断条件 Object Condition
等待方法 wait await
通知方法 notify/notifyAll signal/signalAll

 

读写锁:

独占锁(写锁):一次只能被一个线程占有
共享锁(读锁):该锁可以被多个线程占有

自旋锁

偏向锁

 

 

常用辅助类

Semaphore 信号量

用途:多线程共享资源争夺、并发线程数量控制、多生产者/多消费者模式;

注意事项:

  1. 创建对象时传入的permits仅仅是初值,可通过多次调用release动态增加permits

  2. 创建对象时可传参控制是否公平,默认为非公平

         
方法 可被中断 不会被中断 尝试获得(非阻塞) 指定时间内尝试获得
获得 acquire()
acquire(int permits)
acquireUninterruptibly()
acquireUninterruptibly(int permits)
tryAcquire()
tryAcquire(int permits)
tryAcquire(long timeout, TimeUnit unit)
tryAcquire(int permits, long timeout, TimeUnit unit)
释放 release()
release(int permits)
     

其他方法:

   
方法 含义
availablePermits() 当前可用许可数
drainPermits() 获取并返回立即可用的许可数、并将可用许可数清零
getQueueLength() 取得等待许可的线程数
hasQueuedThreads() 判断是否有线程在等待许可

Exchanger 交换器

用途:2个线程之间传输数据

   
方法 说明
exchange(V x) 没有其他线程来取数据则会阻塞
exchange(V x, long timeout, TimeUnit unit) 指定时间内没有其他线程来取数据,则抛出java.util.concurrent.TimeoutException

CountDownLatch 减法计数器

用途:控制线程间同步,当计数器变为0的时候继续运行,否则阻塞。

注意事项:计数器无法重置

     
方法 说明 抛出异常
await()
await(long timeout, TimeUnit unit)
进入等待状态 InterruptedException
countDown() 计数器-1  

CyclicBarrier 加法计数器

用途:控制线程间同步,当计数器达到指定值的时候继续运行,否则阻塞。

     
方法 说明 抛出异常
await() 进入等待状态 InterruptedException
BrokenBarrierException
await(long timeout, TimeUnit unit) 进入等待状态有限时间 InterruptedException
BrokenBarrierException
TimeoutException
getNumberWaiting() 返回已到达屏障点的线程数,
注意:最后一个线程已到达时会返回0;
即假设parties=3,线程1,2 await()方法之后调用getNumberWaiting()=1,2,而线程3 await()方法之后调用getNumberWaiting()=0
 
getParties() 屏障对象个数  
reset() 重置屏障,等待屏障的其他线程会出现BrokenBarrierException  

CountDownLatch和CyclicBarrier区别:

CountDownLatch使用情况为2个角色互等、CyclicBarrier使用情况为同类互等。

 

Phaser 移相器(CyclicBarrier增强版) since1.7

用途:CyclicBarrier增强版、可动态增减parties计数,可用于线程分组同步控制。

   
方法 说明
arrive() 使getArrivedParties()+1,且不等待其他线程,并重置计数器
awaitAdvance(int phase) 如果参数phase值和当前getPhase()方法返回值一致则等待,否则继续运行,类似旁观者作用,可被中断但不会影响其他线程运行,不抛出InterruptedException
awaitAdvanceInterruptibly(int phase) 如果参数phase值和当前getPhase()方法返回值一致则等待,否则继续运行,可被中断但不会影响其他线程运行,抛出InterruptedException
awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit) 同上,timeout时会抛出TimeoutException
arriveAndAwaitAdvance() 计数不足时会阻塞
arriveAndDeregister() 退出计数,并使parties-1
getPhase() 获取已到达屏障的对象个数
getRegisteredParties() 获取注册的parties数
register() parties+1
bulkRegister(int parties) 批量增加parties
getArrivedParties() 获取已经被使用的parties数
getUnarrivedParties() 获取还未被使用的parties数
forceTermination() 取消屏障,线程继续执行后续,不出现异常
isTerminated() 屏障是否已取消

 

线程池

线程池编码速记口诀:3大方法(实际工作中不推荐直接使用:see《阿里巴巴Java开发手册(终极版)》一、编程规约-(六)并发处理-条目4)、7大参数、4种拒绝策略

 

 

线程池大小/线程数

线程池大小与处理器的利用率之比估算公式:N-threads = N-CPU * U-CPU * (1 + W/C)
其中:
❑N-CPU是处理器的核的数目=Runtime.getRuntime().availableProcessors()
❑U-CPU是期望的CPU利用率(该值应该介于0和1之间)
❑W/C是等待时间与计算时间的比率

 

线程数设定:

  • CPU密集型(计算密集型):

    最大线程数=CPU核数=Runtime.getRuntime().availableProcessors(); 推荐使用Stream接口。

  • IO密集型:

    最大线程数=IO任务的倍数、不能低于IO任务的数量;使用CompletableFuture灵活性更好。

execute和submit方法区别

     
  execute submit
返回值 N Y
异常 默认直接抛出、不能捕获,可通过ThreadFactory方式进行捕获 默认可通过catch (ExecutionException e)捕获

CompletionService

用途:避免FutureTask阻塞缺点,更有效地处理Future返回值。

   
方法 说明
take() 获取已完成任务的Future(非阻塞),但当存在有任务未能完成时 take().get() 仍会阻塞
poll() 获取并移除已完成任务的Future,不存在则返回null(非阻塞)
poll(long timeout, TimeUnit unit) 等待指定时间,无论结果如何均往下执行

 

Fork-Join 分支合并 since1.7

 

2步骤:任务拆分、结果合并。

应用场景:大数据量,好处:提高效率、坏处:产生资源争夺

原理:工作窃取(work-stealing)算法、底层使用双端队列

 

 

集合/队列

 不安全集合类 安全集合类
ArrayList CopyOnWriteArrayList
HashSet CopyOnWriteArraySet
HashMap ConcurrentHashMap
  • 4组API:队列一般可以检测第一个元素是谁!

 方法  第一组会抛出异常  返回一个布尔值,不会抛出异常  延时等待  一直等待(阻塞)
插入 add() offer(e) offer(e,time) put()
取出 remove() poll() poll(time) take()
检查 element() peek() - -

 

非阻塞队列7个

阻塞队列6个

 

 

since 1.8

CompletableFuture 异步回调

函数式编程(java.util.function)

 

lambda表达式:()->{}

4个基本的函数式接口:

  • Function : 有一个输入参数有一个输出参数

  • Consumer:有一个输入参数,没有输出参数

  • Supplier:没有输入参数,只有输出参数

  • Predicate:有一个输入参数,判断是否正确

Stream(java.util.stream)

  • 流:从支持数据处理操作的源生成的一系列元素。

  • 2类流操作:中间操作、终端操作。

  • 流利用内部迭代:迭代通过filter、map、sorted等操作被抽象掉了,filter、map等中间操作会返回一个流,并可以链接在一起,可以用来设置一条流水线,但不会生成结果。

  • forEach和count等终端操作会返回一个非流的值,并处理流水线以返回结果。

  • 流中的元素是按需计算的。

 阅读《Java 8 in Action》、《Java 8函数式编程》

 

单例模式实现对比

       
实现方式 可延迟加载 多线程环境安全 会被反射破坏
饿汉式 N Y Y
懒汉式 Y N Y
懒汉式+synchronized Y Y   但性能低下 Y
DCL懒汉式 Y Y   但可能产生NPE Y
DCL懒汉式+volatile Y Y Y
Holder方式 Y Y Y
枚举类式 N Y N
枚举类+Holder方式 Y Y Y

原子引用

java.util.concurrent.atomic包

ABA问题