concurrent包了

# concurrent包了解

第一部分 Atomic数据类型

这部分都被放在java.util.concurrent.atomic这个包里面,实现了原子化操作的数据类型,包括 Boolean, Integer, Long, 和Referrence这四种类型以及这四种类型的数组类型。

第二部分 锁

这部分都被放在java.util.concurrent.lock这个包里面,实现了并发操作中的几种类型的锁


第三部分 java集合框架中的一些数据结构的并发实现

这部分实现的数据结构主要有List, Queue和Map。


第四部分 多线程任务执行

这部分大体上涉及到三个概念,

Callable     被执行的任务
Executor  执行任务
Future      异步提交任务的返回数据
这部分主要是对线程集合的管理的实现,有CyclicBarrier, CountDownLatch,Exchanger等一些类。


### 1、Atomic数据类型

Atomic数据类型有四种类型:AtomicBoolean, AtomicInteger, AtomicLong, 和AtomicReferrence(针对Object的)以及它们的数组类型,还有一个特殊的AtomicStampedReferrence,它不是AtomicReferrence的子类,而是利用AtomicReferrence实现的一个储存引用和Integer组的扩展类。

####CAS####
种无锁机制,比较并交换, 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无 论哪种情况,它都会在 CAS 指令之前返回该位置的值。CAS 有效地说明了"我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可"。



####AtomicXXXX四个数值类型####
1. value成员都是volatile
2. 基本方法get/set
3. compareAndSet
weakCompareAndSet,
lazySet: 使用Unsafe按照顺序更新参考Unsafe的C++实现
getAndSet:取当前值,使用当前值和准备更新的值做CAS
4. 对于Long和Integer
getAndIncrement/incrementAndGet
getAndDecrement/decrementAndGet
getAndAdd/addAndGet
三组方法都和getAndSet,取当前值,加减之得到准备更新的值,再做CAS,/左右的区别在于返回的是当前值还是更新值。


####关于数组####
1. 没有Boolean的Array,可以用Integer代替,底层实现完全一致,毕竟AtomicBoolean底层就是用Integer实现。
2. 数组变量volatile没有意义,因此set/get就需要Unsafe来做了,方法构成与上面一致,但是多了一个index来指定操作数组中的哪一个元素。
```
AtomicIntegerArray 使用

public class AtomicIntegerArrayDemo {

    static AtomicIntegerArray arr = new AtomicIntegerArray(10);
    public static class AddThread implements Runnable{
        @Override
        public void run(){
            for(int k=0;k<10000;k++){
                arr.getAndIncrement(k%arr.length());
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread[]ts=new Thread[10];
        for(int k=0;k<10;k++){
            ts[k]=new Thread(new AddThread());
        }
        for(int k=0;k<10;k++){ts[k].start();}
        for(int k=0;k<10;k++){ts[k].join();}
        System.out.println(arr);
    }

```


####关于FieldUpdater####
1) 利用反射原理,实现对一个类的某个字段的原子化更新,该字段类型必须和Updater要求的一致,例如如果使用AtomicIntegerFieldUpdater,字段必须是Integer类型,而且必须有volatile限定符。Updater的可以调用的方 法和数字类型完全一致,额外增加一个该类型的对象为参数,updater就会更新该对象的那个字段了。
```
public class AtomicIntegerFieldUpdaterDemo {

    public static void main(String[] args) throws InterruptedException {
        final Container c= new Container();
        for(int i=0;i<1000;i++)
        {
            Task t=new Task(c);
            t.start();
            t.join(); //join方法在start后调用,调用线程会等待被调用的线程执行完成后执行

        }
        System.out.println("============="+c.index);
    }
    static class Container{
        public volatile  int index=0;


        private String name;
    }
}

class Task extends Thread {
    //初始化根据那个Field来进行自增
    private AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterDemo.Container> updater =
            AtomicIntegerFieldUpdater.newUpdater(AtomicIntegerFieldUpdaterDemo.Container.class, "index");

    private AtomicIntegerFieldUpdaterDemo.Container c;

    public Task(AtomicIntegerFieldUpdaterDemo.Container c) {
        this.c = c;
    }
    @Override
    public void run() {
        System.out.println(updater.getAndIncrement(c));
        System.out.println(updater.getAndDecrement(c));
    }
}
```
2) Updater本身为抽象类,但有一个私有化的实现,利用门面模式,在抽象类中使用静态方法创建实现。比如我们使用的时候,利用外观模式来处理,统一调用AtomicIntegerFieldUpdater。
```
AtomicIntegerFieldUpdater<Object> updater = AtomicIntegerFieldUpdater.newUpdater(Object, field);
AtomicIntegerFieldUpdaterDemo.Object c;
```

####AtomicMarkableReference和AtomicStampedReference####
AtomicMarkableReference跟AtomicStampedReference差不多, AtomicStampedReference是使用pair的int stamp作为计数器使用,AtomicMarkableReference的pair使用的是boolean mark。 还是那个水的例子,AtomicStampedReference可能关心的是动过几次,AtomicMarkableReference关心的是有没有被人动过,方法都比较简单。


### 2、锁


####Condition####
在使用signal(类似于notify)通知的时候需要实现按照什么样的顺序来通知。
三种等待方式:不中断,一定时间间隔,等到某个时间点。


####Lock和ReadWriteLock####
两个接口都有一个可重入(ReentrantLock, ReentrantReadWriteLock)的实现。


####LockSupport####
工具类,操作对象是线程,基于Unsafe类实现。基本操作park和unpark。park会把使得当前线程失效(没有提供操作其他线程的,其实是可以实现的),暂时挂起,直到出现以下几种情况中的一种:
1)其他线程调用unpark方法操作该线程   2)该线程被中断    3)park方法立刻返回

####AbstractOwnableSynchronizer, AbstractQueuedSynchronizer, AbstractQueuedLongSynchronizer####

**AbstractQueuedSynchronizer的基本介绍**

节点状态:
SIGNAL(-1):当前结点的后继节点将会是阻塞的(通过park方法),因此当前结点需要唤醒他的后继节点,当他释放或取消后。为了避免竞争,获取同步状态的方法必须抢先表示自己需要信号,然后重新原子的获取。最后可能是获取失败,或者再次被阻塞。
CANCELLED(1):由于超时、中断等原因,当前结点会被取消。取消后,节点不会释放状态。特殊情景下,被取消的节点中的线程将不会再被阻塞
CONDITION(-2):当前结点在一个条件队列中,再被转移之前将不会被作为同步节点。被转移时该值会被设置为0。
PROPAGATE(-3):共享式方式释放同步状态后应该被传播到其他节点。这种设置(仅对head节点)在doReleaseShared方法中确保了可以持续,及时有其他的干预操作。
0:  非以上状态



方法解析
doReleaseShared
共享模式下的释放操作,从队首开始向队尾扩散,如果节点的waitStatu是SIGNAL,就唤醒后继节点,如果waitStatus是0,就设置标记成PROPAGATE。
setHeadAndPropagate
把自己设置为head,并且把释放的状态往下传递,这里采用了链式唤醒的方法,1个节点负责唤醒1个后续节点,直到不能唤醒。当后继节点是共享模式isShared,就调用doReleaseShared来唤醒后继节点。
cancelAcquire
取消获取操作,要把节点从同步队列中去除,通过链表操作将它的前置节点的next指向它的后继节点集合。如果该节点是在队尾,直接删除即可,否则要通知后继节点去获取锁
compareAndSetTail
原子的设置尾节点为当前结点,并链接好前后节点。
shouldParkAfterFailedAcquire
将节点的前驱节点的waitStatus设置为SIGNAL,表示会通知后续节点,这样后续节点才能放心去park,而不用担心被丢失唤醒的通知。
selfInterrupt
执行中断
parkAndCheckInterrupt
阻塞并检查是否中断
acquireQueued
监控Node对象在队列中的变化,如果检测到线程中断,返回true,否则返回false.
tryAcquire
获取锁,是否成功。被其集成的方法实现。
doAcquireInterruptibly
监控Node对象在队列中的变化,如果发现中断,直接break,然后取消获取锁的打算。
doAcquireNanos
限时。
setHeadAndPropagate
把自己设置为head,并且把释放的状态往下传递,这里采用了链式唤醒的方法,1个节点负责唤醒1个后续节点,直到不能唤醒。当后继节点是共享模式isShared,就调用doReleaseShared来唤醒后继节点。
doReleaseShared
从head开始往后检查状态,如果节点是SIGNAL状态,就唤醒它的后继节点。如果是0就标记为PROPAGATE, 等它释放锁的时候会再次唤醒后继节点。
tryAcquire
以独占模式获取的尝试查询对象的状态是否允许以独占模式获取它,如果可以的话,就可以获取它。
独占模式释放他
tryRelease
共享模式
tryAcquireShared/tryReleaseShared

疑问:
1、node独占模式和共享模式之间有什么联系?
是否同时有多个线程进入

共享锁就是 读取锁,多个线程读一个资源并不会对资源有所影响;
杜占锁就是 写入锁,多个线程写入资源时,同一时刻只能有一个线程来写。


AbstractOwnableSynchronizer只是实现了被线程独占这些功能的Synchronizer,并不包含如何管理实现多个线程的同步。包含了一个exclusiveOwnerThread,set/get方法。AbstractQueuedSynchronizer利用Queue的方式来管理线程关于锁的使用和同步,相当于一个锁的管理者。
首先关注四个最核心的方法:
protected boolean tryAcquire(int arg)
protected boolean tryRelease(int arg)
protected int tryAcquireShared(int arg)
protected boolean tryReleaseShared(int arg)
 
前两个用于独占锁,后两者用于共享锁,这四个方法是由子类来实现的,即如何获取和释放锁AbstractQueuedSynchronizer是不参与的,默认实现是不支持,即抛出UnsupportedOperationException。

AbstractQueuedSynchronizer的作用:
 
当 前线程尝试获取锁的时候,AbstractQueuedSynchronizer会先调用tryAcquire或者tryAcquireShared来尝 试获取,如果得到false,那么把当前线程放到等待队列中去,然后再做进一步操作。我们来分析以下6种情况,前三种用于独占锁,后三者用于共享,独占锁 或者共享锁按照等待方式又分为三种:不可中断线程等待,可中断线程等待,尝试限时等待超时放弃。
这6种的方法都含有一个int类型的参数,这个是给上面的tryAcquire这种方法使用的,也就是说它一个自定义的参数,一般用来表示某个自定义的状态。
1) 独占锁,放入队列后,直到成功获取锁,会忽略线程的中断
2) 独占锁,放入队列后,直到成功获取锁或者遇到中断
3)限时 到一定时间后,直接退出(计算的安全的做法不是一次等待,立刻超时,因为一次等待的时间不一定等于预先设定的值,而是多次等待,累计计算比较安全)。

**AbstractQueuedLongSynchronizer**
AbstractQueuedLongSynchronizer和AbstractQueuedSynchronizer的区别在于acquire和release的arg参数是long而不是int类型。
 

####ReentrantLock####
所谓可重入锁,就是当一个thread已经获得一个lock的时候,再次请求该锁的时候,会立即返回。使用AbstractQueuedSynchronizer的子类(Sync, NonfairSync, FairSync)进行锁获取释放的管理。
 
state等于0表示当前没有线程占用锁,下面两个获取锁的过程基本类似,共同的过程是首先检查有没有线程使用该锁,没有的话就占用该并且setState,否则就检查那个占用锁的线程是不是当前线程,如果是的话,仅仅setState,否则就返回false。
Sync#nonfairTryAcquire
```
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
```
FairSync#tryAcquire
```
protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (isFirst(current) &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}
```
对于FairSync,唯一的不同在于isFirst的调用,而UnfairSync则完全不会检查,谁抢到就是谁的。isFirst会检查有没有线程排队,如果没有,当前线程就可以获得锁,如果有队列,就看当前线程是不是排第一个。
```
final boolean isFirst(Thread current) {
    Node h, s;
    return ((h = head) == null ||
            ((s = h.next) != null && s.thread == current) ||
            fullIsFirst(current));
}
```

####ReentrantReadWriteLock####
ReentrantReadWriteLock同时管理共享锁(读取锁)和独占锁(写入锁)。
也对应使用AbstractQueuedSynchronizer的子类(Sync, NonfairSync, FairSync)进行锁获取释放的管理。(名字一样但是实现是不同的)。
Sync类
1) Sync的state是32位的,高位的16位是共享锁的状态,低位的16位是独占锁的状态。
```
/*
 * Note that tryRelease and tryAcquire can be called by
 * Conditions. So it is possible that their arguments contain
 * both read and write holds that are all released during a
 * condition wait and re-established in tryAcquire.
 */

protected final boolean tryRelease(int releases) {
    int nextc = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    // 首先检查独占锁计数,如果是0表示独占锁已经被完全释放,则清除独占锁线程
    // 更新状态
    if (exclusiveCount(nextc) == 0) {
        setExclusiveOwnerThread(null);
        setState(nextc);
        return true;
    } else {
        setState(nextc);
        return false;
    }
}

protected final boolean tryAcquire(int acquires) {
    /*
     * Walkthrough:
     * 1. if read count nonzero or write count nonzero
     *     and owner is a different thread, fail.
     * 2. If count would saturate, fail. (This can only
     *    happen if count is already nonzero.)
     * 3. Otherwise, this thread is eligible for lock if
     *    it is either a reentrant acquire or
     *    queue policy allows it. If so, update state
     *    and set owner.
     */
    Thread current = Thread.currentThread();
    int c = getState();
    int w = exclusiveCount(c);
    if (c != 0) {
        // c != 0表示有共享锁或者独占锁存在,w == 0表示没有独占锁
        // 那么两个条件同时成立表示有共享锁存在,就无法获得独占锁
        // 或者有线程拥有独占锁但不是当前线程,那也无权获得独占锁
        // (Note: if c != 0 and w == 0 then shared count != 0)
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
    }
    // 到了这一步,可能有以下几种情况:
    // 1) c == 0没有任何锁存在(这个时候 w == 0 也成立)
    // 2) 当前线程拥有独占锁,并且还没到锁的最大限制数

    // w == 0是当前线程没有独占锁,属于新申请
    // writerShouldBlock是抽象方法,对于FairSync和UnfairSync有不同实现
    // 该发现检查当前线程申请独占锁应不应该被阻止
    // 对于FairSync,writerShouldBlock会用isFirst检查,
    // 对于isFirst,如果如果没人排队,或者你是第一个排队的,或者fullIsFirst就返回true
    // 对于fullIsFirst,不是很理解
    // 对于UnfairSync,writerShouldBlock永远返回false,因为没有排队的概念(体现Unfair)
    if ((w == 0 && writerShouldBlock(current)) ||
        !compareAndSetState(c, c + acquires))
        return false;

    // 获取独占锁成功,设置独占锁线程
    setExclusiveOwnerThread(current);
    return true;
}

// 这里使用HoldCounter类型的ThreadLocal变量存储当前线程拥有的共享锁的计数
// cachedHoldCounter缓存最近一次成功获取共享锁的线程的ThreadLocal变量
protected final boolean tryReleaseShared(int unused) {
    HoldCounter rh = cachedHoldCounter;
    Thread current = Thread.currentThread();
    if (rh == null || rh.tid != current.getId())
        rh = readHolds.get();
    // tryDecrement()返回拥有的共享锁的计数,大于0则并且更新计数(减1)。
    if (rh.tryDecrement() <= 0)
        throw new IllegalMonitorStateException();

    // 更新共享锁的计数
    for (;;) {
        int c = getState();
        int nextc = c - SHARED_UNIT; // 高位的共享锁计数减一
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}

protected final int tryAcquireShared(int unused) {
    /*
     * Walkthrough:
     * 1. If write lock held by another thread, fail
     * 2. If count saturated, throw error
     * 3. Otherwise, this thread is eligible for
     *    lock wrt state, so ask if it should block
     *    because of queue policy. If not, try
     *    to grant by CASing state and updating count.
     *    Note that step does not check for reentrant
     *    acquires, which is postponed to full version
     *    to avoid having to check hold count in
     *    the more typical non-reentrant case.
     * 4. If step 3 fails either because thread
     *    apparently not eligible or CAS fails,
     *    chain to version with full retry loop.
     */
    Thread current = Thread.currentThread();
    int c = getState();
    // 其他线程正在使用独占锁
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    // 共享锁计数到达最大限制
    if (sharedCount(c) == MAX_COUNT)
        throw new Error("Maximum lock count exceeded");

    // 类似于writerShouldBlock,readerShouldBlock是抽象方法,有不同实现,
    // 检查是不是阻止当前线程共享锁的申请
    // 对于UnfairSync,为了防止独占锁饿死的情况,如果发现队列中第一个排队的是独占锁申请,
    // 就是block当前共享锁的申请
    // 对于FairSync,同样使用isFirst检查当前线程
    if (!readerShouldBlock(current) &&
        compareAndSetState(c, c + SHARED_UNIT)) {
        HoldCounter rh = cachedHoldCounter;
        if (rh == null || rh.tid != current.getId())
            cachedHoldCounter = rh = readHolds.get();
        rh.count++;
        return 1;
    }

    // 针对CAS失败或者一些不太常见的失败的情况
    // 思想:实现常规版本和完整版本(包含所有情况),在常规版本失败的情况下调用完整版本, 提高效率
    return fullTryAcquireShared(current);
}
```
fullTryAcquireShared
增加计数缓存,以及红色部分
```
/**
 * Full version of acquire for reads, that handles CAS misses
 * and reentrant reads not dealt with in tryAcquireShared.
 */
final int fullTryAcquireShared(Thread current) {
    /*
     * This code is in part redundant with that in
     * tryAcquireShared but is simpler overall by not
     * complicating tryAcquireShared with interactions between
     * retries and lazily reading hold counts.
     */
    HoldCounter rh = cachedHoldCounter;
    if (rh == null || rh.tid != current.getId())
        rh = readHolds.get();
    for (;;) {
        int c = getState();
        int w = exclusiveCount(c);
        // 红色部分表示没有占用共享锁,新申请共享锁
        if ((w != 0 && getExclusiveOwnerThread() != current) ||
            ((rh.count | w) == 0 && readerShouldBlock(current)))
            return -1;
        if (sharedCount(c) == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        if (compareAndSetState(c, c + SHARED_UNIT)) {
            cachedHoldCounter = rh; // cache for release
            rh.count++;
            return 1;
        }
    }
}
```
####ReadLock和WriteLock####


### 3、同步集合类


####synchronizedMap####
Collections.synchronizedMap(new HashMap());线程安全的map。通过mutex(多线程环境下,无论map的写入或者读取都需要获取mutex锁,会导致所有对map操作的线程进入等待,直到mutex锁可用)实现对map的互斥,但是在多线程环境中的性能表现并不算太好。


####ConcurrentMap####

**锁分段技术**
HashTable容器在竞争激烈的并发环境下表现出效率低下的原因,是因为所有访问HashTable的线程都必须竞争同一把锁,那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。这里“按顺序”是很重要的,否则极有可能出现死锁,在ConcurrentHashMap内部,段数组是final的,并且其成员变量实际上也是final的,但是,仅仅是将数组声明为final的并不保证数组成员也是final的,这需要实现上的保证。这可以确保不会出现死锁,因为获得锁的顺序是固定的。
! [](https://i.imgur.com/pbfGeX8.png)

ConcurrentHashMap的基本思想是采取分块的方式加锁,分块数由参数“concurrencyLevel”来决定(和HashMap中的“initialCapacity”类似,实际块数是第一个大于concurrencyLevel的2的n次方)。每个分块被称为Segment,Segment的索引方式和HashMap中的Entry索引方式一致(hash值对数组长度取模)。

对Segment加锁的方式很简单,直接把Segment定义为ReentrantLock的子类。同时Segment又是一个特定实现的hash table。

hash获取的问题:
```
private int hash(Object k) {
    int h = hashSeed;

    if ((0 != h) && (k instanceof String)) {
        return sun.misc.Hashing.stringHash32((String) k);
    }

    h ^= k.hashCode();

    // Spread bits to regularize both segment and index locations,
    // using variant of single-word Wang/Jenkins hash.
    h += (h <<  15) ^ 0xffffcd7d;
    h ^= (h >>> 10);
    h += (h <<   3);
    h ^= (h >>>  6);
    h += (h <<   2) + (h << 14);
    return h ^ (h >>> 16);
}
private transient final int hashSeed = randomHashSeed(this);

private static int randomHashSeed(ConcurrentHashMap instance) {
    if (sun.misc.VM.isBooted() && Holder.ALTERNATIVE_HASHING) {
        return sun.misc.Hashing.randomHashSeed(instance);
    }

    return 0;
}
```
如果key是String,则使用stringHash32直接对key做hash;如果key不是String,则对key的hashCode,进行复杂的位运算,使得二进制数中1的分布更加均匀。这样做的好处,就是尽量做到hash值不重复。因为hash不重复的数据,在寻址时,能够通过key的hash值,一次性找到,而如果hash多次重复,则数据会以链表的形式存储,根据key获取值时,只能遍历查找效率极低的链表(不懂的话,先略过,一会儿再回头来看)。
注意:sun.misc.Hashing.randomHashSeed后续会调用java.util.Random.nextInt方法。Random类以其多线程环境下不友好而闻名:它有一个Atomic类型的成员变量private final AtomicLong seedfield。Atomic类型在多线程竞争程度较低或者适中的场景下性能表现较好,但在竞争激烈的场景下性能很差。上面的代码可以看到,ConcurrentHash仅在系统设置了jdk.map.althashing.threshold才启用。


```
static final class Segment<K,V> extends ReentrantLock implements Serializable
```
GET代码
```
public V get(Object key) {
    //定义的segment就是把原来Array<Entry>拆成小块了
    Segment<K,V> s; // manually integrate access methods to reduce overhead
    HashEntry<K,V>[] tab;
    //获取当前key的hash值
    int h = hash(key);
    // 根据Segment的索引((h >>> segmentShift) & segmentMask)算出在Segment[]上的偏移量  
    long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
    // 若Segment存在,则继续查找table[]的索引位置;  
    // 根据table的索引((tab.length - 1) & h)算出在table[]上的偏移量,循环链表找出结果  
    if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
        (tab = s.table) != null) {
        for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
                 (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
             e != null; e = e.next) {
            K k;
            if ((k = e.key) == key || (e.hash == h && key.equals(k)))
                return e.value;
        }
    }
    return null;
}
```

PUT代码
可以看到,读取的时候没有调用的Segment的获取锁的方法,而是通过hash值定位到Entry,然后遍历Entry的链表。为什么这里不用加锁呢?看看HashEntry的代码就会明白了。
```
static final class HashEntry<K,V> {
    final int hash;
    final K key;
    volatile V value;
    volatile HashEntry<K,V> next;
}
```
value和next属性是带有volatile修饰符的,可以大胆放心的遍历和比较。

```
public V put(K key, V value) {
    Segment<K,V> s;
    if (value == null)
        throw new NullPointerException();
    int hash = hash(key);
    int j = (hash >>> segmentShift) & segmentMask;
    if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
         (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
        s = ensureSegment(j);
    return s.put(key, hash, value, false);
}
```

####CountDownLatch####
倒计时、门栓。主线程在CountDownLatch等待,当所有的检查任务全部完成后,主线程才能继续执行。

####CyclicBarrier####
和CountDownLatch的基本相似,唯一的强项是可以传入一个action,当达标时。

####LockSupport####
park和unpark方法来替代suspend和resume(suspend可能导致线程永久卡死;resume可能导致线程无法继续运行),与Object.wait相比,它不需要先获得某个对象的锁,也不会抛出InterruptedException异常。park和unpark的执行顺序并不会影响线程的继续执行。


####BlockingQueue####
阻塞队列实现数据共享通道。BlockingQueue是一个接口,并有ArrayBlockingQueue和LinkedBlockingQueue实现。

####ConcurrentLinkedQueue####
使用CAS(内存位置(V)、预期原值(A)和新值(B))保证线程的安全。CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。

####CopyOnWriteArrayList和CopyOnWriteArraySet####
适用于读多写少的操作; CopyOnWrite在写入操作的时候,进行一次自我复制,换句话说,当整个List需要修改时,并不修改原有的内容,而是对原有的数据进行一次复制,并将修改的内容写入到副本里。写完之后,再将修改完的副本替换为原来的数据。修改的时候利用锁,修改完毕后在newElements中加入新元素,最后使用新数组替换老数组即可。因为array变量时volatile类型,修改完之后,读操作能立刻获得。

####SkipList####
跳表类似Redis中跳表。跳表是有序的,



### 4、线程池

不需要重复的制造轮子,复用即可。

####设定线程池的大小####
ExecutorService作为接口;

####计划任务的实现####
ScheduledExecutorService类;

scheduleAtFixedRate  任务按照 initialDelay + n*period的时间点来执行。

scheduleWithFixedDelay 下一个任务在上一个任务完成后的时间差才开始执行。


####线程池的原理####

分析ThreadPoolExecutor类:
corePoolSize:指定线程池中线程数量;
maximumPoolSize:线程池中最大的线程数量;
keepAliveTime:超过corePoolSize的线程可以生存多久时间;
unit:时间单位;
workQueue:任务队列,被提交但尚未被执行的任务;
threadFactory:线程工厂,用于创建线程;
handler:拒绝策略,任务过多来不及处理时,如何拒绝任务;

解析workQueue:
直接提交队列>
有界的任务队列>
无界的任务队列>
优先任务队列>

拒绝策略:
直接抛出异常>
运行当前被丢弃的线程>
丢弃最老的一个请求>
丢弃无法处理的请求,不予任何处理>

ThreadFactory 线程池;
ThreadPoolExecutor扩展线程池,可提供beforeExecute()|afterExecuto()|terminated()三个接口可以对线程进行控制;


**合理设置线程池的大小**

Ncpu *Ucpu*(1+W/C)

Ncpu表示cpu的总数量;Ucpu表示可用cpu的数量;W/C表示等待时间与计算时间的比值;
综上,可以发现计算时间越短,线程池越大。如果计算时间过大,那么线程池应该小点;

**线程池中需要堆栈**
线程池默认情况下不会打印出堆栈信息,在查找时非常痛苦的。
1、放弃使用submit(),改用execute();
2、改造submit(),比如:
```
Future re=pools.submit(new DivTask(100,i));
re.get();
```
3、封装submit、execute,自己创建一个clientTrace()方法,并在异常的时候打印。
```
public Exception clientTrace(){
    return new Exception("Client stack trace");
}
```

**Fork/Join框架**
分而治之的逻辑,使用fork提交任务,join等待直到所有的任务都完成。
ForkJoinTask支持fork()分解和join()等待任务。RecursiveAction(没有返回值)和RecusiveTask(可携带返回值)继承。
```
package com.paic.fcs.remit.utils.demo;

import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

/**
 * Created by LAIXIAO820 on 2018/1/24.
 * version
 */
public class CountTask extends RecursiveTask {
    private static final int THRESHOLD=1000;
    private long start;
    private long end;

    public CountTask(long start, long end){
        this.start = start;
        this.end = end;
    }


    @Override
    protected Long compute() {
        long sum=0;
        boolean canCompute = (end-start) <THRESHOLD;
        if (canCompute) {
            for (long i=start;i<=end;i++) {
                sum+=i;
            }
        }else {
            long step = (start+end)/100;

            ArrayList<CountTask> subTasks = new ArrayList<CountTask>();
            long pos = start;
            for (int i=0;i<100;i++){

                //一个线程处理的数量
                long lastOne= pos + step;
                if (lastOne>end) lastOne =end;

                CountTask subTask = new CountTask(pos, lastOne);
                pos+=step+1;
                subTasks.add(subTask);
                subTask.fork();
            }

            for (CountTask task : subTasks) {
                sum += (long)task.join();
            }
         }
        return sum;
    }

    public static void main(String[] args){
        ForkJoinPool pool = new ForkJoinPool();
        CountTask task = new CountTask(0, 1000L);
        ForkJoinTask<Long> result= pool.submit(task);

        long res = 0;
        try {
            res = result.get();
            System.out.println("sum="+res);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}
```
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值