[Java多线程]-学习多线程须要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)

前言:刚学习了一段机器学习,最近须要重构一个java项目,又赶过来看java。大可能是线程代码,没办法,那时候总以为多线程是个很难的部分不多用到,因此一直没下决定去啃,那些年留下的坑,老是得本身跳进去填一次。html

思路:大概看了线程相关的一些知识,对线程的运行机制,同步机制,以及整个系统都作一个全面的了解。在深刻每个部分去看一下线程及其相关包的源码作深刻了解。java

目标:线程,并发包(线程池,并发的数据结构,锁,原子类)程序员

   经过一些资料的查看最终把目标定位在线程和并发包上,线程是核心,并发包是辅助工具,用于多线程运行时的并发问题。其实这样看来多线程并无不少的东西,支持并发的数据结构用于保证数据的安全性,各类锁机制用来保证类,对象,方法,属性的并发安全。它的难点主要是在运用上,各类锁机制的运用,会给系统带来负担,也会给程序性能带来影响,可是同时又要保证数据的同步。锁机制使用的强度和位置,直接决定了并发系统的好坏。web

java中文APIhttp://www.javaweb.cc/help/JavaAPI1.6/overview-summary.html,下面具体看一下这些包里面有哪些东西。算法

主要涉及到下面几个包:数组

线程相关:java.lang下面的几个类安全

接口摘要
Runnable Runnable 接口应该由那些打算经过某一线程执行其实例的类来实现。
类摘要
Thread 线程 是程序中的执行线程。
ThreadGroup 线程组表示一个线程的集合。
ThreadLocal<T> 该类提供了线程局部 (thread-local) 变量。

并发包相关:数据结构

1.   Java.util.concurrent包多线程

接口摘要
BlockingDeque<E> 支持两个附加操做的 Queue,这两个操做是:获取元素时等待双端队列变为非空;存储元素时等待双端队列中的空间变得可用。
BlockingQueue<E> 支持两个附加操做的 Queue,这两个操做是:获取元素时等待队列变为非空,以及存储元素时等待空间变得可用。
Callable<V> 返回结果而且可能抛出异常的任务。
CompletionService<V> 将生产新的异步任务与使用已完成任务的结果分离开来的服务。
ConcurrentMap<K,V> 提供其余原子 putIfAbsentremovereplace 方法的 Map
ConcurrentNavigableMap<K,V> 支持 NavigableMap 操做,且以递归方式支持其可导航子映射的 ConcurrentMap
Delayed 一种混合风格的接口,用来标记那些应该在给定延迟时间以后执行的对象。
Executor 执行已提交的 Runnable 任务的对象。
ExecutorService Executor 提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行情况而生成 Future 的方法。
Future<V> Future 表示异步计算的结果。
RejectedExecutionHandler 没法由 ThreadPoolExecutor 执行的任务的处理程序。
RunnableFuture<V> 做为 Runnable 的 Future
RunnableScheduledFuture<V> 做为 Runnable 的 ScheduledFuture
ScheduledExecutorService 一个 ExecutorService,可安排在给定的延迟后运行或按期执行的命令。
ScheduledFuture<V> 一个延迟的、结果可接受的操做,可将其取消。
ThreadFactory 根据须要建立新线程的对象。
类摘要
AbstractExecutorService 提供 ExecutorService 执行方法的默认实现。
ArrayBlockingQueue<E> 一个由数组支持的有界阻塞队列
ConcurrentHashMap<K,V> 支持获取的彻底并发和更新的所指望可调整并发的哈希表。
ConcurrentLinkedQueue<E> 一个基于连接节点的无界线程安全队列
ConcurrentSkipListMap<K,V> 可缩放的并发 ConcurrentNavigableMap 实现。
ConcurrentSkipListSet<E> 一个基于 ConcurrentSkipListMap 的可缩放并发 NavigableSet 实现。
CopyOnWriteArrayList<E> ArrayList 的一个线程安全的变体,其中全部可变操做(addset 等等)都是经过对底层数组进行一次新的复制来实现的。
CopyOnWriteArraySet<E> 对其全部操做使用内部 CopyOnWriteArrayList 的 Set
CountDownLatch 一个同步辅助类,在完成一组正在其余线程中执行的操做以前,它容许一个或多个线程一直等待。
CyclicBarrier 一个同步辅助类,它容许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。
DelayQueue<E extends Delayed> Delayed 元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。
Exchanger<V> 能够在对中对元素进行配对和交换的线程的同步点。
ExecutorCompletionService<V> 使用提供的 Executor 来执行任务的 CompletionService
Executors 此包中所定义的 ExecutorExecutorServiceScheduledExecutorServiceThreadFactory 和 Callable 类的工厂和实用方法。
FutureTask<V> 可取消的异步计算。
LinkedBlockingDeque<E> 一个基于已连接节点的、任选范围的阻塞双端队列
LinkedBlockingQueue<E> 一个基于已连接节点的、范围任意的 blocking queue
PriorityBlockingQueue<E> 一个无界阻塞队列,它使用与类 PriorityQueue 相同的顺序规则,而且提供了阻塞获取操做。
ScheduledThreadPoolExecutor ThreadPoolExecutor,它可另行安排在给定的延迟后运行命令,或者按期执行命令。
Semaphore 一个计数信号量。
SynchronousQueue<E> 一种阻塞队列,其中每一个插入操做必须等待另外一个线程的对应移除操做 ,反之亦然。
ThreadPoolExecutor 一个 ExecutorService,它使用可能的几个池线程之一执行每一个提交的任务,一般使用 Executors 工厂方法配置。
ThreadPoolExecutor.AbortPolicy 用于被拒绝任务的处理程序,它将抛出 RejectedExecutionException.
ThreadPoolExecutor.CallerRunsPolicy 用于被拒绝任务的处理程序,它直接在 execute 方法的调用线程中运行被拒绝的任务;若是执行程序已关闭,则会丢弃该任务。
ThreadPoolExecutor.DiscardOldestPolicy 用于被拒绝任务的处理程序,它放弃最旧的未处理请求,而后重试 execute;若是执行程序已关闭,则会丢弃该任务。
ThreadPoolExecutor.DiscardPolicy 用于被拒绝任务的处理程序,默认状况下它将丢弃被拒绝的任务。
枚举摘要
TimeUnit TimeUnit 表示给定单元粒度的时间段,它提供在这些单元中进行跨单元转换和执行计时及延迟操做的实用工具方法。

2.   java.util.concurrent.locks并发

接口摘要
Condition Condition 将 Object 监视器方法(waitnotify 和 notifyAll)分解成大相径庭的对象,以便经过将这些对象与任意 Lock 实现组合使用,为每一个对象提供多个等待 set(wait-set)。
Lock Lock 实现提供了比使用 synchronized 方法和语句可得到的更普遍的锁定操做。
ReadWriteLock ReadWriteLock 维护了一对相关的,一个用于只读操做,另外一个用于写入操做。
类摘要
AbstractOwnableSynchronizer 能够由线程以独占方式拥有的同步器。
AbstractQueuedLongSynchronizer 以 long 形式维护同步状态的一个 AbstractQueuedSynchronizer 版本。
AbstractQueuedSynchronizer 为实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关同步器(信号量、事件,等等)提供一个框架。
LockSupport 用来建立锁和其余同步类的基本线程阻塞原语。
ReentrantLock 一个可重入的互斥锁 Lock,它具备与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
ReentrantReadWriteLock 支持与 ReentrantLock 相似语义的 ReadWriteLock 实现。
ReentrantReadWriteLock.ReadLock ReentrantReadWriteLock.readLock() 方法返回的锁。
ReentrantReadWriteLock.WriteLock ReentrantReadWriteLock.writeLock() 方法返回的锁。

3.   java.util.conturrent.atomic

类摘要
AtomicBoolean 能够用原子方式更新的 boolean 值。
AtomicInteger 能够用原子方式更新的 int 值。
AtomicIntegerArray 能够用原子方式更新其元素的 int 数组。
AtomicIntegerFieldUpdater<T> 基于反射的实用工具,能够对指定类的指定 volatile int 字段进行原子更新。
AtomicLong 能够用原子方式更新的 long 值。
AtomicLongArray 能够用原子方式更新其元素的 long 数组。
AtomicLongFieldUpdater<T> 基于反射的实用工具,能够对指定类的指定 volatile long 字段进行原子更新。
AtomicMarkableReference<V> AtomicMarkableReference 维护带有标记位的对象引用,能够原子方式对其进行更新。
AtomicReference<V> 能够用原子方式更新的对象引用。
AtomicReferenceArray<E> 能够用原子方式更新其元素的对象引用数组。
AtomicReferenceFieldUpdater<T,V> 基于反射的实用工具,能够对指定类的指定 volatile 字段进行原子更新。
AtomicStampedReference<V> AtomicStampedReference 维护带有整数“标志”的对象引用,能够用原子方式对其进行更新。

对每一个包里的部分类进行结构解析:

  1.   Java.util.concurrent:这个包里,主要经常使用到的是数据结构Queue,MAP,List,和线程池

    *&*Queue队列相关的类图:

  

      *&*List和Set相关的类图:List和Set在并发包中的实现类有copyOwriteArrayList,CopyOnWriteArraySet,ConcurrentSkipListSet,三个类

   

    *&*Map相关的类图:并发包中与Map相关的包括ConcurrentHashMap,ConcurrentSkipListMap两个类

  

    这些都是和数据结构相关的,其中蓝色的部分表示的是并发包的内容灰色部分表示其余包(大部分是util)中的内容。其中省略了一部份内容类和接口,由于不少不经常使用,并且用的时候一部分也不是做为一个数据结构来用的。这也不是这部分关注的重点。咱们主要关注并发包相关的内容。这三张类图,应该可让你们对并发包的数据结构有一个大体的了解,并发包还有一个内容就是线程池。

    *&*线程池类图:线程池重最终有两个实现类ThreadPoolExeutor和SheduleThreadPoolExeutor.可是咱们通常不直接去实现这两个类去建立一个线程池,咱们一般用Exeutors这个类来建立一个线程池,这个类中把线程池的建立和管理进行了封装,咱们只须要运用这个类就能够建立一个线程池并进行管理。另一个接口ThreadFactory主要是用来建立线程的,实现这个接口咱们就拥有了一个线程工厂,建立线程更方便。

 

 2.   Java.util.concurrent.Locks:(如下说明摘自API)为锁和等待条件提供一个框架的接口和类,它不一样于内置同步和监视器。该框架容许更灵活地使用锁和条件,但以更难用的语法为代价

  Lock 接口支持那些语义不一样(重入、公平等)的锁规则,能够在非阻塞式结构的上下文(包括 hand-over-hand 和锁重排算法)中使用这些规则。主要的实现是 ReentrantLock

  ReadWriteLock 接口以相似方式定义了一些读取者能够共享而写入者独占的锁。此包只提供了一个实现,即 ReentrantReadWriteLock,由于它适用于大部分的标准用法上下文。但程序员能够建立本身的、适用于非标准要求的实现。

  Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用 Object.wait 访问的隐式监视器相似,但提供了更强大的功能。须要特别指出的是,单个 Lock 可能与多个 Condition 对象关联。为了不兼容性问题,Condition 方法的名称与对应的 Object 版本中的不一样。

  AbstractQueuedSynchronizer 类是一个很是有用的超类,可用来定义锁以及依赖于排队阻塞线程的其余同步器。

  AbstractQueuedLongSynchronizer 类提供相同的功能但扩展了对同步状态的 64 位的支持。

  前面二者都扩展了类 AbstractOwnableSynchronizer(一个帮助记录当前保持独占同步的线程的简单类)。LockSupport 类提供了更低级别的阻塞和解除阻塞支持,这对那些实现本身的定制锁类的开发人员颇有用。

  

3.   Java.util.concurrent.atomic:(如下内容摘自API)原子类这部分没有很复杂的类关系,主要是对基础的int,long,bolean变量,以及相关的数组和对象引用,提供了原子访问和更新的类。

  原子访问和更新的内存效果通常遵循如下可变规则,正如 The Java Language Specification, Third Edition (17.4 Memory Model) 中的声明:

    • get 具备读取 volatile 变量的内存效果。
    • set 具备写入(分配)volatile 变量的内存效果。
    • 除了容许使用后续(但不是之前的)内存操做,其自身不施加带有普通的非 volatile 写入的从新排序约束,lazySet 具备写入(分配)volatile 变量的内存效果。在其余使用上下文中,当为 null 时(为了垃圾回收),lazySet 能够应用不会再次访问的引用。
    • weakCompareAndSet 以原子方式读取和有条件地写入变量但 建立任何 happen-before 排序,所以不提供与除 weakCompareAndSet 目标外任何变量之前或后续读取或写入操做有关的任何保证。
    • compareAndSet 和全部其余的读取和更新操做(如 getAndIncrement)都有读取和写入 volatile 变量的内存效果。

  除了包含表示单个值的类以外,此包还包含 Updater 类,该类可用于获取任意选定类的任意选定 volatile 字段上的 compareAndSet 操做

  类 AtomicBooleanAtomicIntegerAtomicLong 和 AtomicReference 的实例各自提供对相应类型单个变量的访问和更新。

  AtomicReferenceFieldUpdaterAtomicIntegerFieldUpdater 和 AtomicLongFieldUpdater 是基于反射的实用工具,能够提供对关联字段类型的访问。它们主要用于原子数据结构中,该结构中同一节点(例如,树节点的连接)的几个 volatile 字段都独立受原子更新控制。这些类在如何以及什么时候使用原子更新方面具备更大的灵活性,但相应的弊端是基于映射的设置较为拙笨、使用不太方便,并且在保证方面也较差。

   AtomicIntegerArrayAtomicLongArray 和 AtomicReferenceArray 类进一步扩展了原子操做,对这些类型的数组提供了支持。这些类在为其数组元素提供 volatile 访问语义方面也引人注目,这对于普通数组来讲是不受支持的。

  AtomicMarkableReference 类将单个布尔值与引用关联起来。例如,能够在数据结构内部使用此位,这意味着引用的对象在逻辑上已被删除。

  AtomicStampedReference 类将整数值与引用关联起来。例如,这可用于表示与更新系列对应的版本号。

 

 花了两天时间看着一段的API,稍微整理了一下,大部份内容仍是来自API,可是我我的以为这种东西听起来很难入门,其实一大部分缘由是咱们没用从整体上去把握它,从最开始的接口和类的清单表中可能不少东西没有接触过,其实那都不是问题,当看到类图的时候清楚地看到他们之间的父子关系,其实最终须要咱们去掌握的类不是不少。 

  我我的认为,数据结构(queue,Map,List)这部分是最容易掌握的,由于它的内部机制已经实现了,咱们只须要知道在什么场景须要使用它,何时的数据须要用这种并发安全的数据结构在通过不断地使用就能掌握(深刻理解当我没说,哈哈)。线程池这部分也很容易掌握,咱们只要熟悉Exeutors类,掌握里面的方法,就足够熟练的运用对线程池。而后是原子类,原子类访问的变量是volited修饰的,原子类实际上就是对数据进行了操做的原子性(操做是一个不可分割的总体,ex:updape包括读取数据,修改数据,写回数据三个步骤,普通的操做就不能保证update操做的原子性)一致性的封装,保证了对这些数据操做的时候是线程安全的。最难的应该是在锁上,加锁会形成线程阻塞,高并发状态下是否该用锁,和在什么地方用锁,锁的粒度很关键,原本只须要在数据上加锁,而咱们却加在了方法上,或者对象上,那对性能的影响可能不是一星半点。各类锁的用法和技巧,带来的差别,弊端都须要清楚的知道。

  但愿能给像我同样还未入门的朋友带来一点帮助,如今已经基本了解了框架,后面就是对这些部分的深刻探索,从具体的应用中看差别,深刻源码和底层的VM实现看原理,我相信一步步去作必定会逐渐上手。

相关文章
相关标签/搜索