所谓无底深渊,下去,也是前程万里。 ——木心《素履之往》
在研读 JDK 源码之前,先了解 JDK 几个核心包的设计思想,将有助于我们理解当初的设计者们的意图,让我们更能体会到设计者的良苦用心。
提供利用 Java 编程语言进行程序设计的基础类。最重要的类是 Object(类层次结构的根)和 Class(其实例表示正在运行的应用程序中的类)。
把基本类型的值当成一个对象来表示通常很有必要。包装器类 Boolean、Character、Integer、Long、Float 和 Double 就是用于这个目的。例如,一个 Double 类型的对象包含了一个类型为 double 的字段,这表示如果引用某个值,则可以将该值存储在引用类型的变量中。这些类还提供了大量用于转换基值的方法,并支持一些标准方法,比如 equals 和 hashCode。Void 类是一个非实例化的类,它保持一个对表示基本类型 void 的 Class 对象的引用。
类 Math 提供了常用的数学函数,比如正弦、余弦和平方根。类似地,类 String 和 StringBuffer 提供了常用的字符串操作。
类 ClassLoader、Process、Runtime、SecurityManager 和 System 提供了管理类的动态加载、外部进程创建、主机环境查询(比如时间)和安全策略实施等“系统操作”。
类 Throwable 包含了可能由 throw 语句抛出的对象。Throwable 的子类表示错误和异常。
java.nio.charset.Charset 类的规范描述了字符编码的命名约定,以及每个 Java 平台实现必须支持的标准编码集。
包含 collection 框架、遗留的 collection 类、事件模型、日期和时间工具、国际化和各种实用的工具类(字符串标记生成器、随机数生成器和位数组)。
限于篇幅和重要性,我们这里只讨论最常用的 collection 容器类库的设计规划。
Java平台包括一个集合框架。集合是代表一组对象的对象(例如经典的Vector类)。集合框架是用于表示和操作集合的统一体系结构,使集合可以独立于实现细节进行操作。
集合框架的主要优点是:
集合框架包括:
分为两组。最基本的接口java.util.Collection具有以下子类或接口:
其他集合接口基于java.util.Map,而不是真实的集合。但是,这些接口包含集合视图操作,使它们可以作为集合进行操作。map具有以下子类:
集合接口中的许多修改方法都标记为可选。允许实现不执行这些操作中的一项或多项,如果尝试执行,则会抛出运行时异常(UnsupportedOperationException)。每个实现的文档必须指定支持哪些可选操作。引入了一些术语以帮助该规范:
一些实现限制了可以存储哪些元素(或在Maps中,键和值)。可能的限制包括要求元素:
尝试添加违反实现限制的元素会导致运行时异常,通常是ClassCastException,IllegalArgumentException或NullPointerException。尝试删除或测试是否存在违反实现限制的元素会导致异常。一些受限制的集合允许这种用法。
实现collection接口的类通常以Implementation-style Interface的形式来命名。下表总结了通用实现:
通用实现支持集合接口中的所有可选操作,并且对它们可能包含的元素没有限制。它们是不同步的,但是Collections类包含称为同步包装器的静态工厂,可用于将同步添加到许多未同步的集合中。所有新的实现都有fail-fast迭代器,该迭代器检测无效的并发修改,并且快速而干净地失败(而不是行为异常)。
AbstractCollection,AbstractSet,AbstractList,AbstractSequentialList和AbstractMap类提供了核心集合接口的基本实现,以最大程度地减少实现它们所需的工作量。这些类的API文档精确地描述了每种方法的实现方式,因此,在特定实现的基本操作得以执行的情况下,实现者知道必须重写哪些方法。
使用来自多个线程的集合的应用程序必须经过仔细地编程。通常,这称为并发编程。 Java平台包括对并发编程的广泛支持。
集合是如此频繁地使用,以至于各种并发友好接口和集合的实现都包含在API中。这些类型超出了前面讨论的同步包装程序的范围,可提供并发编程中经常需要的功能。
这些并发感知接口可用:
可以使用以下并发感知实现类。
主要设计目标是生产一种尺寸更小且更重要的是“概念重量”的API。至关重要的是,新功能与当前的Java程序员似乎并没有太大区别。它必须增加现有设施,而不是更换它们。同时,新的API必须足够强大才能提供先前描述的所有优点。
为了使核心接口的数量保持较小,接口不会尝试捕获诸如可变性,可修改性和可缩放性之类的细微差别。相反,核心接口中的某些调用是可选的,从而使实现可以引发UnsupportedOperationException来指示它们不支持指定的可选操作。集合实现者必须清楚地记录实现支持哪些可选操作。
为了使每个核心接口中的方法数量保持较小,接口仅在满足以下任一条件时才包含方法:
至关重要的是,所有合理的馆藏表现形式必须能够良好地互操作。这包括数组,如果不更改语言就不能直接实现Collection接口。因此,该框架包括以下方法:使集合能够移动到数组中,将数组视为集合,将map视为集合。
从JDK1.5版本开始提供:在并发编程中很常用的实用工具类。该包涵盖几个小的、已标准化的可扩展框架,以及一些提供有用功能的类,没有这些类,这些功能会很难实现或实现起来冗长乏味。
下面简要描述主要的组件。
Executor 是一个简单的标准化接口,用于定义类似于线程的自定义子系统,包括线程池、异步 IO 和轻量级任务框架。
根据所使用的具体 Executor 类的不同,任务们可能在新创建的线程中、已有的任务执行线程中或者调用 execute() 的线程中执行,并且可能顺序或并发执行。
ExecutorService 管理任务的排队和安排,并允许受控的关闭。ScheduledExecutorService 子接口及相关的接口添加了对延迟的和定期任务执行的支持。ExecutorService 提供了安排异步执行的方法,可执行由 Callable 表示的任何函数,结果类似于 Runnable。
Future 返回函数的结果,允许确定执行是否完成,并提供取消任务执行的方法。
RunnableFuture 是拥有 run 方法的 Future,run 方法执行时将设置其结果。
类 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 提供可调的、灵活的线程池。Executors 类提供大多数 Executor 的常见类型和配置的工厂方法,以及使用它们的几种实用工具方法。其他基于 Executor 的实用工具包括具体实现类 FutureTask,它提供了 Future 的常见可扩展实现,以及 ExecutorCompletionService 有助于协调对异步任务组的处理。
ForkJoinPool类提供了一个Executor,主要用于处理ForkJoinTask及其子类的实例。这些类使用工作窃取调度程序,该任务调度程序可满足符合计算密集型并行处理中经常存在的限制的任务的高吞吐量。
ConcurrentLinkedQueue 类提供了高效的、可伸缩的、线程安全的非阻塞 FIFO 队列。ConcurrentLinkedDeque类与此类似,但是还支持Deque接口。
java.util.concurrent 包中的五个实现都支持扩展的 BlockingQueue 接口。该接口定义了 put 和 take 的阻塞版本:
这些不同的类覆盖了生产者-消费者、消息传递、任务并行执行和相关并发设计的大多数常见模型的上下文。
扩展接口TransferQueue及其实现LinkedTransferQueue引入了同步的传输方法(以及相关功能),生产者可以选择阻塞等待其消费者。
BlockingDeque 接口继承了 BlockingQueue,以支持 FIFO 和 LIFO(基于栈)操作。LinkedBlockingDeque 类就提供了这样一个实现。
TimeUnit 类为指定和控制基于超时的操作提供了多层粒度(包括纳秒级)。该包中的大多数类除了包含不确定的等待之外,都包含了基于超时机制的操作。
在使用超时的所有场景中,超时规定了在表明已超时前,该方法应等待的最少时间。超时后,实现类们会“尽力”检测超时。当然了,在检测到超时和超时后再次真正地执行线程之间可能还要经过一段不确定的时间。接受超时期参数的所有方法将小于等于 0 的值视为根本不会等待。要“永远”等待,可以使用 Long.MAX_VALUE 值。
五个类可辅助实现常见的专用同步语法。
除队列外,此包还提供了设计用于多线程上下文中的容器实现:
当期望许多线程访问同一个给定容器时,ConcurrentHashMap 通常优于同步的 HashMap,ConcurrentSkipListMap 通常优于同步的 TreeMap。当期望的读请求数和遍历请求远大于对list的更新时,CopyOnWriteArrayList 优于同步的 ArrayList。
此包中与某些类一起使用的 “Concurrent” 前缀是一种速记,暗示着与类似的“同步化”的类有所不同。例如Hashtable 和 Collections.synchronizedMap(new HashMap()) 是同步化的,但 ConcurrentHashMap 则是“并发的(Concurrent)”。并发容器是线程安全的,但不受单个独占锁的管理。在 ConcurrentHashMap 这一特定场景下,它可以安全地允许任意次数的并发读,以及数目可调的并发写。适用场景如下:
大多数并发容器的实现(包括大多数队列)与常规的 java.util 包中的约定也不同,因为它们的迭代器Iterators和Spliterators提供了弱一致的,而不是fast-fail的遍历:
只有写操作HB读操作时,才保证一个线程写结果对另一个线程的读是可见的。synchronized 和 volatile 构造以及Thread.start() 和 Thread.join() 方法可以形成HB关系。尤其是:
java.util.concurrent 中所有类的方法及其子包扩展了这些对更高级别同步的保证。尤其是:
该包的接口和类提供了用于锁和等待条件的框架,这些条件不同于内置的同步和监视器。该框架允许使用锁和条件来实现更大的灵活性,但以更复杂的语法为代价。
Lock 接口支持那些语义不同(重入、公平等)的锁规则,可以在非阻塞式结构的上下文(包括 hand-over-hand 和锁重排算法)中使用这些规则。主要的实现为 ReentrantLock。
ReadWriteLock 接口以类似方式定义了一些读线程可以共享而写线程独占的锁。此包只提供了一个实现,即 ReentrantReadWriteLock,因为它适用于大部分的标准用法上下文。但程序员可以创建自己的、适用于非标准要求的实现。
Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用 Object.wait 访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,一个 Lock 可能与多个 Condition 对象关联。为了避免兼容性问题,Condition 方法的名称与对应的 Object 版本中的不同。
AbstractQueuedSynchronizer 类是一个非常有用的超类,可用来定义锁以及依赖于排队阻塞线程的其他同步器。 AbstractQueuedLongSynchronizer 类提供相同的功能但扩展了对同步状态的 64 位的支持。两者都扩展了类 AbstractOwnableSynchronizer(一个帮助记录当前保持独占同步的线程的简单类)。LockSupport 类提供了更低级别的阻塞和解除阻塞支持,这对那些实现自己的定制锁的开发人员很有用。
通过数据流、序列化和文件系统提供系统输入和输出,Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入的数据源和输出的目标对象。
除非另有说明,否则向此包的任何类或接口中的构造方法或方法传递 null 参数时,都将抛出 NullPointerException。
流可以理解为数据的序列:
流支持很多种格式,比如:基本类型、对象、数组等。下图展示了该包的内容从数据源、操作目标等角度可大致分类如下:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。