前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >3分钟速读原著《Java并发编程的艺术》(四)

3分钟速读原著《Java并发编程的艺术》(四)

作者头像
cwl_java
发布2019-10-26 20:43:21
4760
发布2019-10-26 20:43:21
举报
文章被收录于专栏:cwl_Javacwl_Java

第七章 Java中的13个原子操作类

代码语言:javascript
复制
Java从JDK1.5开始提供了Java.util.concurrent.atomic,这个包当中的原子操作类提供了一种用法简单,性能高效,线程安全的更新一个变量的方式
1.基本类型
  • 1.1 AtomicBoolean:原子更新布尔类型
  • 1.2 AtomicInteger:原子更新整型
  • 1.3 AtomicLong:原子更新长整形

方法基本都一样,包含了加减乘除,都是 线程安全的

2.数组类型
  • 2.1AtomicIntegerArray : 整形数组当中的元素
  • 2.2 AtomicLongArray : Long类型数组
  • 2.3 AtomicReferenceArray : 引用类型数组

备注:数组的value通过构造方法传递进去,然后AtomicIntegerArray会将当前数组复制一份,所以当AtomicIntegerArray对内部数组元素进行修改时,不会影响传入的数组

3.原子更新引用类型
  • 3.1 原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子更新多个变量,就需要使用这个原子更新引用类型提供的类
    • AtomicReference:原子更新引用类型
    • AtomicReferenceFieldUpdater:原子更新引用类型当中的字段
    • AtomicMarkableReference:原子更新带有标记位的引用类型
  • 3.2 原子更新字段类
    • AtomicIntegerFieldUpdater : 原子隔音整形的字段的更新器
    • AtomicLongFieldUpdater : 原子更新长整型的更新器
    • AtomicStampedReference : 原子更新带有版本号的引用类型

第八章 Java中的并发工具类

1.等待多线程完成的CountDownLatch

CountDownLatch允许一个或多个线程等待其他线程完成操作 需求场景:我们需要解析一个Excel里有多个sheet的数据,考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完成之后,程序需要提示解析完成,要 实现主线程等待所有线程完成sheet的解析操作,最简单的做法就是使用Join()方法

小结.Join的本质有点类似于插队,让其他线程都等着,必须要让这个Join的线程先把事情办完,才能执行其他的线程,其实现原理是不停的检查Join线程是否存活,如果Join线程存活则让当前线程永远等待,其中wait(0)表示的就是永远等待下去.

  • 1.1 当Join线程终止值周,线程的this.notifyAll()方法会被调用,调用notifyAll()方法是在JVM当中实现的,所以在JDK当中断点是看不到的,可以通过查看JVM源码进行查看
  • 1.2 在JDK1.5之后的并发包当中提供的CountDownLatch也可以实现Join功能,并且比Join功能更多
  • 1.3 CountDownLatch的构造器可以传入一个int类型的参数作为计数器,如果想等待N个点完成,这里就传入N
  • 1.4 假如某个解析sheet的线程处理的比较慢,我们不可能让主线程一直等待,所以可以使用另外一个带指定时间的await方法,这个方法等待了特定时间之后,就不会再阻塞当前线程
2.同步屏障CyclicBarrier
  • 2.1 简单来说CyclicBarrier做的事情就是让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行
  • 2.2 CyclicBarrier默认的构造方法可以传入一个int类型的参数,其参数表示屏障要拦截的线程数量,每个线程调用await方法高速CyclicBarrier我已经到达了屏障
  • 2.3 应用场景:CyclicBarrier可以用于多线程计算数据,最后合并计算结果的场景.例如用一个Excel保存了用户所有银行流水,每个sheet保存一个账户近一年的每笔银行流水,现在需要统计用户的日均银行流水,先用多线程处理美格尔sheet的银行流水,都执行完之后,得到每个sheet的日均银行流水,最后,在用barrierAction用这些线程的计算结果,计算出整个Excel的日均银行流水

实现方式:使用线程池创建4个线程,分别计算每个sheet里的数据,每个sheet计算结果是sum,再有实现了CyclicBarrier的线程来尽心汇总4个sheet计算出的结果

3.CyclicBarrier和CountDownLatch的区别

CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法进行重置,所以相对而言,CyclicBarrier能够处理更为复杂的业务场景.例如,如果计算发生错误,可以重置计数器,并让线程重新执行一次

4.控制并发线程数的Semaphore
  • 4.1 应用场景:可以用于流量控制.假如有一个需求,要读取很多数据,同时又有很多需要写入,就可以使用Semaphore来进行流量控制
  • 4.2 Semaphore的构造器也是可以传入一个int类型的参数,该参数表示的是允许的最大并发数量
5.线程之间交换数据的Exchanger
  • 5.1 Exchanger是一个用于线程之间协作的工具类,使用方式就是当线程A调用了exchange方法,那么它就会一直等待线程B也会性了exchange方法,当两个线程都达到了同步点时,这两个线程就可以开始交换数据了

如果两个线程有一个没有执行exchange()方法,则会一直等待,如果担心有特殊情况发生,避免一直等待,可以使用exchange()传入最大等待时长的参数

第9章 Java中的线程池

1.线程池的好处
  • 1.1 降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗
  • 1.2 提高响应速度.当任务到达时,任务可以不需要等到线程创建就能够立即执行
  • 1.3 提高线程的可管理性线程池可以对线程进行统一分配,调优和监控
2.线程池的实现原理
  • 2.1 线程池判断核心线程池当中的线程是否都在执行任务.是则执行当前线程,否则下一步
  • 2.2 线程池判断工作队列是否已满,未满则添加该任务到工作队列,已满则进入下一步
  • 2.3 判断所有线程是否都处于工作状态,如果没有则创建一个新的工作线程来执行任务
3.ThreadPoolExecutor执行分成了4种情况
  • 3.1 如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(创建新线程需要获取全局锁)
  • 3.2 如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue
  • 3.3 如果队列已经满了,则创建新的线程来处理任务(创建新线程将获取全局锁)
  • 3.4 如果创建新线程将使得当前运行的线程超出maximumPoolSize,任务将会被拒绝,并且调用RejectedExecutionHandler.rejectedExecution()方法

在线程池的使用当中要尽可能的避免获取全局锁,大多数情况下,都会执行步骤2,该步骤是不需要获取全局锁的,全局锁可能会导致一个严重的可伸缩瓶颈,会导致整体的性能下降

4.工作线程

线程池创建线程时,会将线程封装成工作线程Worker,Worker在执行完任务后,还会循环获取工作队列当中的任务来进行执行

5.线程任务的执行
  • 5.1 在execute()方法中创建一个线程时,会让这个线程执行当前任务
  • 5.2 这个线程执行完任务之后,会反复的从BlockingQueue获取任务来执行
6.线程池的使用
  • 6.1 线程池的创建:创建参数
    • ①CorePoolSize:线程池的基本大小
    • ②RunnableTaskQueue:任务队列,用于保存等待执行的任务的阻塞队列
      • a) ArrayBlockingQueue:基于数组结构的有界阻塞队列,按照FIFO原则排序
      • b) LinkedBlockingQueue:基于链表结构的阻塞队列,按照FIFO排序
      • c) SynchronousQueue:一个不储存元素的阻塞队列
      • d) PriorityBlockingQueue:具有优先级的无线阻塞队列
    • ③MaximumPooolSize:线程池的最大数量:线程池允许创建的最大线程数,这个数量应该等于线程池的基本大小+阻塞队列当中的线程池,但是如果使用的是无界的任务队列,那么这个参数就没有什么效果了
    • ④ThreadFactory:创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字
    • ⑤RejectedExectionHandler:饱和策略,当队列和线程池都已经满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务,有四种处理方式
      • a) AbortPolicy:直接抛出异常
      • b) CallerRunsPolicy:只用调用者所在线程来运行任务
      • c) DIscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务
      • d) DIscardPolicy:不处理,丢弃掉
    • ⑥饱和策略的其他实现方式:
      • a) KeepAliveTime:设置线程活动的保持时间,设置它可能会过期的时间,用于保证所有线程的活性
      • b) TimeUnit:线程活动保持时间的单位
  • 6.2 向线程池提交任务
    • ①Execute():不需要返回值的任务
    • ②Submit():提交需要返回值的任务.线程池会返回一个future累心对象,这个future可以对象可以用于判断任务是否执行成功,并且可以通过future的get()方法来获取返回值
  • 6.3 关闭线程池
    • ①Shutdown或shutdownNow方法来关闭线程池.原理是便利线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程
    • ②只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true.当所有任务关闭之后,才表示线程池已经关闭.此时isTerminaed方法会返回true.
  • 6.4 合理的配置线程池
    • ①根据任务特性来分析线程池的配置
      • a) 任务的性质:CPU密集型/IO密集型任务/混合型任务
        • CPU密集型的任务配置应尽可能的小,假设CPU有N个,则配置N+1个
        • IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,假设CPU有N个,则配置2*N,混合型的任务
      • b) 任务优先级:高/中/低
      • c) 任务的执行时间:长/中/段
      • d) 任务的依赖性:是否依赖其他系统资源.例如数据库的连接
      • e) 执行时间不同的任务可以交给不同规模的线程池来处理,或者可以使用优先级队列,让执行时间短的任务先执行
      • f) 依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,等待时间越长,则CPU空闲时间就越长,那么线程数应该设置的越大,这样才能更好的利用CPU
      • g) 建议使用有界队列,有界队列能增加系统的稳定性和预警能力,可以根据需要设置的大一点

备注:如果一直有优先级高的任务提交到队列当中,那么优先级低的任务将永远不会被执行

  • 6.5 线程池的监控

如果在系统中大量使用线程池,则有必要对线程池进行监控,方便在出现问题的时候更方便找到问题所在,监控线程池时可以使用以下属性

  • 6.5.1 TaskCount:线程池需要执行的任务数量
  • 6.5.2 CompletedTaskCount:线程池在运行过程中已完成的数量,限制小于等于taskCount
  • 6.5.3 LargestPoolSize:线程池当中曾经创建过的最大的线程数量.通过这个数据可以知道线程池是否曾经满过
  • 6.5.4 GetPoolSize:线程池的线程数量.如果线程池不销毁的话,线程池的线程不会自动销毁,所以这个大小只曾不减少
  • 6.5.5 GetActiveCount:获取活动的线程数

备注:通过拓展线程池来自定义线程池,重写线程池的beforeExecute,afterExecute和terminated方法,也可以在任务执行之前,执行后和线程池关闭前执行一些代码来进行监控.这些方法在线程池当中都是空方法

第10章 Executor框架

背景介绍:在Java中,使用线程来异步执行任务.Java线程的创建与销毁需要一定的开销,如果我们为每一个任务都创建一个新的线程来执行,那么这些线程的创建与销毁将消耗大量的计算资源.同时,为每一个任务创建一个新的线程来执行,这种策略可能会处于高负荷状态的应用最终崩溃 Java的线程即是工作单元,也是执行机制.从JDK5开始,就已经把工作单元和执行机制分离开来.工作单元包括Runnable和Callable,而执行机制有Executor框架提供

1.Exevutor接口的实现类
  • 1.1 ThreadPoolExecutor是线程池的核心实现类,用来被提交的任务
  • 1.2 ScheduledThreadPoolExecutor 可以在给定的延迟后运行命令,或者定期执行命令

简而言之:执行线程需要去实现Runnable或者是Callable接口

2.3种ThreadPoolExector
  • 2.1 FixedThreadPool:需要限制当前线程数量的应用场景,它使用于负载比较重的服务器
  • 2.2 SingleThreadExector:适用于需要保证顺序的执行各个人物,并且在任意时间点,不会有多个线程是活动的应用场景
  • 2.3 CachedThreadPool : 适用于执行很多的短期异步任务的小程序,或者是负载比较轻的服务器
3.2种ScheduledThreadPoolExecutoor
  • 3.1 ScheduledThreadPoolExecutor :包含若干个线程的ScheduledThreadPoolExecutor
  • 3.2 SinggleThreadScheduledExecutor : 只包含一个县城的ScheduledThreadPoolExecutor
4.Future接口
代码语言:javascript
复制
线程执行完成之后,会返回一个Future接口的实现类FutureTask用于返回执行的结果

备注:不管是Runnable接口还是Callable接口的实现类,都可以被ThreadPoolExecutor或者是Scheduled-ThreadPoolExector执行,它们之间的区别就是Runnbale接口不会返回结果,而Callable可以返回结果

5.线程池的3种类型
  • 5.1 FixedThreadPool:可重用固定线程数的线程池,使用无界队列LinkedBlockingQueue作为线程池的工作队列
  • 5.2 使用无界队列的影响:
    • ①当线程池中的线程数达到corePoolSize之后,新任务将在无界队列当中等待,因此线程池当中的线程数不会超过corePoolSize
    • ②使用无界队列时,maximumPoolSize将是一个无效参数
    • ③KeepAliveTime也将是一个无效参数
    • ④运行中的FixedThreadPool,即是未执行方法shutdown或者shutdownNow不会拒绝任务
6.ScheduledThreadPoolExecutor详解

ScheduledThreadPoolExecutor继承自ThreadPoolExecutor.主要用于给定延迟之后运行任务,或者定期执行任务,功能跟Timer类似,单ScheduledThreadPoolExecutor功能更强大,它包含三个成员变量

  • 6.1 Long类型的time,表示该任务将要被执行的具体时间
  • 6.2 Long类型period 表示任务执行的间隔周期
7.FutureTask简介
代码语言:javascript
复制
FutureTask实现了Future接口和Runnable接口,包含三种状态
  • 7.1 未启动.FutureTask.run()方法执行之前
  • 7.2 已启动.FutureTask.run()方法执行过程中
  • 7.3 已完成.FutureTask.run()方法执行完成之后
  • 7.4 FutureTask.cancel方法的调用情况
    • ①未启动时调用,该任务永远不会被执行
    • ②启动时调用,会中断该线程
    • ③已完成时调用,会返回false,表示任务取消失败
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-05-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第七章 Java中的13个原子操作类
    • 1.基本类型
      • 2.数组类型
        • 3.原子更新引用类型
        • 第八章 Java中的并发工具类
          • 1.等待多线程完成的CountDownLatch
            • 2.同步屏障CyclicBarrier
              • 3.CyclicBarrier和CountDownLatch的区别
                • 4.控制并发线程数的Semaphore
                  • 5.线程之间交换数据的Exchanger
                  • 第9章 Java中的线程池
                    • 1.线程池的好处
                      • 2.线程池的实现原理
                        • 3.ThreadPoolExecutor执行分成了4种情况
                          • 4.工作线程
                            • 5.线程任务的执行
                              • 6.线程池的使用
                              • 第10章 Executor框架
                                • 1.Exevutor接口的实现类
                                  • 2.3种ThreadPoolExector
                                    • 3.2种ScheduledThreadPoolExecutoor
                                      • 4.Future接口
                                        • 5.线程池的3种类型
                                          • 6.ScheduledThreadPoolExecutor详解
                                            • 7.FutureTask简介
                                            相关产品与服务
                                            云开发 CloudBase
                                            云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
                                            领券
                                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档