缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。所有的缓冲区都具有四个属性来提供关于其所包含的数据元素的信息。它们是:
容量(Capacity)
缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变。
上界(Limit)
缓冲区的第一个不能被读或写的元素。或者说,缓冲区中现存元素的计数。
位置(Position)
下一个要被读或写的元素的索引。位置会自动由相应的get()和put()函数更新。
标记(Mark)
一个备忘位置。调用mark()来设定mark=postion。调用reset()设定position=mark。标记在设定前是未定义的(undefined)。
select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
目前常用的IO通信模型主要有以下四种:
这些IO模型都是要靠底层操作系统进行支持,应用程序只是提供相应的实现,对操作系统进行调用。本文将介绍这四种IO模型及Java对这四种IO模型的支持。
FutureTask,可取消的异步计算任务。利用开始和取消计算的方法、查询计算是否完成的方法和获取计算结果的方法,此类提供了对Future的基本实现。仅在计算完成时才能获取结果;如果计算尚未完成,则阻塞get方法。一旦计算完成,就不能再重新开始或取消计算。可使用 FutureTask包装Callable或Runnable对象。因为FutureTask实现了Runnable,所以可将FutureTask提交给Executor执行。
ThreadPoolExecutor,Java线程池。使用线程池可以降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗;可以提高响应速度,当任务到达时,任务可以不需要等待线程创建就能立即执行;可以提高线程的可管理性,防止无限制的创建线程,消耗系统资源;控制任务执行的并发量。
ThreadLocal线程局部变量,使得各线程能够保持各自独立的一份对象。通常被定义为类的静态类变量。
ThreadLocal类本身定义了有get(), set(), remove()和initialValue()方法。前面三个方法是public的,initialValue()是protected的,主要用于我们在定义ThreadLocal对象的时候根据需要来重写。这样我们初始化这么一个对象在里面设置它的初始值时就用到这个方法。ThreadLocal变量因为本身定位为要被多个线程来访问,它通常被定义为static变量。
多个变量被cpu加载在同一个缓存行中,当在多线程环境下,多个变量被不同的cpu执行,导致缓存行失效而引起的大量的缓存命中率降低。
缓存一致性协议MESI协议中,每个CPU的Cache控制器不仅知道自己的读写操作,而且也监听其它CPU的读写操作。每个Cache line所处的状态根据本核和其它核的读写操作在4个状态间进行迁移。其他CPU的写会导致本CPU的Cache无效,下次访问时如果CPU访问的内存数据不在CpuCache中,这就产生了Cache Line miss问题,此时CPU不得不发出新的加载指令,从内存中获取数据。一旦CPU要从内存中访问数据就会产生一个较大的时延,程序性能显著降低。为此我们不得不提高Cache命中率,也就是充分发挥局部性原理。
Java的采用的是共享内存模型,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信。Java线程之间的通信由Java内存模型(JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。
从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
ReentrantReadWriteLock,读写锁。维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的。
与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程)。当访问读写比恰当的共享数据时,使用读-写锁所允许的并发性将带来更大的性能提高。
Condition将Object监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待set(wait-set)。其中,Lock替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用。Condition实例实质上被绑定到一个锁上。要为特定Lock实例获得Condition实例,请使用其newCondition()方法。
ReentrantLock,一个可重入的独占锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
Semaphore,信号量。用于控制同时访问特定资源的线程数量,来保证合理的使用特定资源。比如:有10个数据库连接,有30个线程都需要使用连接,Semaphore可以控制只有10个线程能够获取连接,其他线程需要排队等待,当已经获取到连接的线程释放连接,排队的线程才能够去申请获取。