Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每一个小任务结果后获得大任务结果的框架。java
咱们再经过Fork和Join这两个单词来理解下Fork/Join框架,Fork就是把一个大任务切分为若干子任务并行的执行,Join就是合并这些子任务的执行结果,最后获得这个大任务的结果。好比计算1+2+。。+10000,能够分割成10个子任务,每一个子任务分别对1000个数进行求和,最终汇总这10个子任务的结果。git
重作一道Java面试题(Fork/Join)github
http://ifeve.com/easy-happens-before/面试
synchronized、大部分锁,众所周知的一个功能就是使多个线程互斥/串行的(共享锁容许多个线程同时访问,如读锁)访问临界区,但他们的第二个功能 —— 保证变量的可见性 —— 常被遗忘。编程
简单介绍下。相对于内存,CPU的速度是极高的,若是CPU须要存取数据时都直接与内存打交道,在存取过程当中,CPU将一直空闲,这是一种极大的浪费,妈妈说,浪费是很差的,因此,现代的CPU里都有不少寄存器,多级cache,他们比内存的存取速度高多了。某个线程执行时,内存中的一份数据,会存在于该线程的工做存储中(working memory,是cache和寄存器的一个抽象,这个解释源于《Concurrent Programming in Java: Design Principles and Patterns, Second Edition》§2.2.7,原文:Every thread is defined to have a working memory (an abstraction of caches and registers) in which to store values. 有很多人以为working memory是内存的某个部分,这多是有些译做将working memory译为工做内存的缘故,为避免混淆,这里称其为工做存储,每一个线程都有本身的工做存储),并在某个特定时候回写到内存。单线程时,这没有问题,若是是多线程要同时访问同一个变量呢?内存中一个变量会存在于多个工做存储中,线程1修改了变量a的值何时对线程2可见?此外,编译器或运行时为了效率能够在容许的时候对指令进行重排序,重排序后的执行顺序就与代码不一致了,这样线程2读取某个变量的时候线程1可能尚未进行写入操做呢,虽然代码顺序上写操做是在前面的。这就是可见性问题的由来。数组
咱们没法枚举全部的场景来规定某个线程修改的变量什么时候对另外一个线程可见。但能够制定一些通用的规则,这就是happens-before。它是一个偏序关系,Java内存模型中定义了许多Action,有些Action之间存在happens-before关系(并非全部Action两两之间都有happens-before关系)。“ActionA happens-before ActionB”这样的描述很扰乱视线,是否是?OK,换个描述,若是ActionA happens-before ActionB,咱们能够记做hb(ActionA,ActionB)或者记做ActionA < ActionB,这货在这里已经不是小于号了,它是偏序关系,是否是隐约有些离散数学的味道,不喜欢?嗯,我也不喜欢,so,下面都用hb(ActionA,ActionB)这种方式来表述。安全
从Java内存模型中取两条happens-before关系来瞅瞅:数据结构
An unlock on a monitor happens-before every subsequent lock on that monitor. A write to a volatile field happens-before every subsequent read of that volatile.
对一个monitor的解锁操做happens-before后续对同一个monitor的加锁操做”、“对某个volatile字段的写操做happens-before后续对同一个volatile字段的读操做”……莫名其妙、不知所云、不能理解……就是这个心情。是否是说解锁操做要先于锁定操做发生?这有违常规啊。确实不是这么理解的。happens-before规则不是描述实际操做的前后顺序,它是用来描述可见性的一种规则,下面我给上述两条规则换个说法:多线程
若是线程1解锁了monitor a,接着线程2锁定了a,那么,线程1解锁a以前的写操做都对线程2可见(线程1和线程2能够是同一个线程)。 若是线程1写入了volatile变量v(这里和后续的“变量”都指的是对象的字段、类字段和数组元素),接着线程2读取了v,那么,线程1写入v及以前的写操做都对线程2可见(线程1和线程2能够是同一个线程)。并发
是否是很简单,瞬间以为这篇文章弱爆了,说了那么多,其实就是在说“若是hb(a,b),那么a及以前的写操做在另外一个线程t1进行了b操做时都对t1可见(同一个线程就不会有可见性问题,下面再也不重复了)”。虽然弱爆了,但还得善始善终,是否是,继续来,再看两条happens-before规则:
All actions in a thread happen-before any other thread successfully returns from a join() on that thread. Each action in a thread happens-before every subsequent action in that thread.
通俗版:
线程t1写入的全部变量(全部action都与那个join有hb关系,固然也包括线程t1终止前的最后一个action了,最后一个action及以前的全部写入操做,因此是全部变量),在任意其它线程t2调用t1.join()成功返回后,都对t2可见。 线程中上一个动做及以前的全部写操做在该线程执行下一个动做时对该线程可见(也就是说,同一个线程中前面的全部写操做对后面的操做可见) 大体都是这个样子的解释。
happens-before关系有个很重要的性质,就是传递性,即,若是hb(a,b),hb(b,c),则有hb(a,c)。
Java内存模型中只是列出了几种比较基本的hb规则,在Java语言层面,又衍生了许多其余happens-before规则,如ReentrantLock的unlock与lock操做,又如AbstractQueuedSynchronizer的release与acquire,setState与getState等等。
- java.util.concurrent.atomic
原子操做类至关于泛化的volatile变量,可以支持原子读取-修改-写操做。
好比AtomicInteger表示一个int类型的数值,提供了get和set方法,
这些volatile类型的变量在读取与写入上有着相同的内存语义。
原子操做类共有13个类,在Java.util.concurrent.atomic包下,能够分为四种类型的原子更新类:
原子更新基本类型、原子更新数组类型、原子更新引用和原子更新属性。
AtomicInteger
AtomicBoolean
AtomicLong
核心实现是使用了unsafe的循环CAS。
unsafe也只提供了3种CAS:compareAndSwapObject,compareAndSwapInt,compareAndSwapLong
由于原子操做类只支持3种,其余的基本类型可使用这三个代替。
AtomicIntegerArray
AtomicLongArray
AtomicReferenceArray
构造方法传入的是个数组,原子类会复制一个数组。
AtomicReference
AtomicReferenceFieldUpdater
AtomicMarkableReference
AtomicIntegerFieldUpdater
AtomicLongFieldUpdater
AtomicStampedReference 能够解决CAS的ABA问题
CountDownLatch是一个同步工具类,它容许一个或多个线程一直等待,直到其余线程的操做执行完后再执行。在Java并发中,countdownlatch的概念是一个常见的面试题,因此必定要确保你很好的理解了它。在这篇文章中,我将会涉及到在Java并发编 程中跟CountDownLatch相关的如下几点:
CountDownLatch是什么? CountDownLatch如何工做? 在实时系统中的应用场景 应用范例 常见的面试题
CountDownLatch是什么
CountDownLatch是在java1.5被引入的,跟它一块儿被引入的并发工具类还有CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue,它们都存在于java.util.concurrent包下。CountDownLatch这个类可以使一个线程等待其余线程完成各自的工做后再执行。例如,应用程序的主线程但愿在负责启动框架服务的线程已经启动全部的框架服务以后再执行。
CountDownLatch是经过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了本身的任务后,计数器的值就会减1。当计数器值到达0时,它表示全部的线程已经完成了任务,而后在闭锁上等待的线程就能够恢复执行任务。
CyclicBarrier和CountDownLatch同样,都是关于线程的计数器。
一个计数信号量。从概念上讲,信号量维护了一个许可集。若有必要,在许可可用前会阻塞每个 acquire(),而后再获取该许可。每一个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。可是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采起相应的行动。拿到信号量的线程能够进入代码,不然就等待。经过acquire()和release()获取和释放访问许可。
相关方法
acquire
public void acquire()
throws InterruptedException
今后信号量获取一个许可,在提供一个许可前一直将线程阻塞,不然线程被中断。获取一个许可(若是提供了一个)并当即返回,将可用的许可数减 1。
若是没有可用的许可,则在发生如下两种状况之一前,禁止将当前线程用于线程安排目的并使其处于休眠状态:
某些其余线程调用此信号量的 release() 方法,而且当前线程是下一个要被分配许可的线程;或者
其余某些线程中断当前线程。
若是当前线程:
被此方法将其已中断状态设置为 on ;或者
在等待许可时被中断。
则抛出 InterruptedException,而且清除当前线程的已中断状态。
抛出:
InterruptedException - 若是当前线程被中断
release
public void release()
释放一个许可,将其返回给信号量。释放一个许可,将可用的许可数增长 1。若是任意线程试图获取许可,则选中一个线程并将刚刚释放的许可给予它。而后针对线程安排目的启用(或再启用)该线程。
不要求释放许可的线程必须经过调用 acquire() 来获取许可。经过应用程序中的编程约定来创建信号量的正确用法。
Exchanger能够在两个线程之间交换数据,只能是2个线程,他不支持更多的线程之间互换数据。
当线程A调用Exchange对象的exchange()方法后,他会陷入阻塞状态,直到线程B也调用了exchange()方法,而后以线程安全的方式交换数据,以后线程A和B继续运行