前言:刚学习了一段机器学习,最近须要重构一个java项目,又赶过来看java。大可能是线程代码,没办法,那时候总以为多线程是个很难的部分不多用到,因此一直没下决定去啃,那些年留下的坑,老是得本身跳进去填一次。html
思路:大概看了线程相关的一些知识,对线程的运行机制,同步机制,以及整个系统都作一个全面的了解。在深刻每个部分去看一下线程及其相关包的源码作深刻了解。java
目标:线程,并发包(线程池,并发的数据结构,锁,原子类)。程序员
经过一些资料的查看最终把目标定位在线程和并发包上,线程是核心,并发包是辅助工具,用于多线程运行时的并发问题。其实这样看来多线程并无不少的东西,支持并发的数据结构用于保证数据的安全性,各类锁机制用来保证类,对象,方法,属性的并发安全。它的难点主要是在运用上,各类锁机制的运用,会给系统带来负担,也会给程序性能带来影响,可是同时又要保证数据的同步。锁机制使用的强度和位置,直接决定了并发系统的好坏。web
java中文API:http://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> | 提供其余原子 putIfAbsent、remove、replace 方法的 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 | 根据须要建立新线程的对象。 |
枚举摘要 | |
TimeUnit | TimeUnit 表示给定单元粒度的时间段,它提供在这些单元中进行跨单元转换和执行计时及延迟操做的实用工具方法。 |
2. java.util.concurrent.locks包并发
接口摘要 | |
Condition | Condition 将 Object 监视器方法(wait 、notify 和 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) 中的声明:
除了包含表示单个值的类以外,此包还包含 Updater 类,该类可用于获取任意选定类的任意选定 volatile 字段上的 compareAndSet 操做
类 AtomicBoolean
、AtomicInteger
、AtomicLong
和 AtomicReference
的实例各自提供对相应类型单个变量的访问和更新。
类AtomicReferenceFieldUpdater
、AtomicIntegerFieldUpdater
和 AtomicLongFieldUpdater
是基于反射的实用工具,能够提供对关联字段类型的访问。它们主要用于原子数据结构中,该结构中同一节点(例如,树节点的连接)的几个 volatile 字段都独立受原子更新控制。这些类在如何以及什么时候使用原子更新方面具备更大的灵活性,但相应的弊端是基于映射的设置较为拙笨、使用不太方便,并且在保证方面也较差。
AtomicIntegerArray
、AtomicLongArray
和 AtomicReferenceArray
类进一步扩展了原子操做,对这些类型的数组提供了支持。这些类在为其数组元素提供 volatile 访问语义方面也引人注目,这对于普通数组来讲是不受支持的。
AtomicMarkableReference
类将单个布尔值与引用关联起来。例如,能够在数据结构内部使用此位,这意味着引用的对象在逻辑上已被删除。
AtomicStampedReference
类将整数值与引用关联起来。例如,这可用于表示与更新系列对应的版本号。
花了两天时间看着一段的API,稍微整理了一下,大部份内容仍是来自API,可是我我的以为这种东西听起来很难入门,其实一大部分缘由是咱们没用从整体上去把握它,从最开始的接口和类的清单表中可能不少东西没有接触过,其实那都不是问题,当看到类图的时候清楚地看到他们之间的父子关系,其实最终须要咱们去掌握的类不是不少。
我我的认为,数据结构(queue,Map,List)这部分是最容易掌握的,由于它的内部机制已经实现了,咱们只须要知道在什么场景须要使用它,何时的数据须要用这种并发安全的数据结构在通过不断地使用就能掌握(深刻理解当我没说,哈哈)。线程池这部分也很容易掌握,咱们只要熟悉Exeutors类,掌握里面的方法,就足够熟练的运用对线程池。而后是原子类,原子类访问的变量是volited修饰的,原子类实际上就是对数据进行了操做的原子性(操做是一个不可分割的总体,ex:updape包括读取数据,修改数据,写回数据三个步骤,普通的操做就不能保证update操做的原子性)一致性的封装,保证了对这些数据操做的时候是线程安全的。最难的应该是在锁上,加锁会形成线程阻塞,高并发状态下是否该用锁,和在什么地方用锁,锁的粒度很关键,原本只须要在数据上加锁,而咱们却加在了方法上,或者对象上,那对性能的影响可能不是一星半点。各类锁的用法和技巧,带来的差别,弊端都须要清楚的知道。
但愿能给像我同样还未入门的朋友带来一点帮助,如今已经基本了解了框架,后面就是对这些部分的深刻探索,从具体的应用中看差别,深刻源码和底层的VM实现看原理,我相信一步步去作必定会逐渐上手。