Concurrent包总结

2018-03-06  本文已影响0人  怒放的生命_浴火重生

一、AtomicXXX系列类

用于变量的同步场景,包括AtomicBoolean、AtomicInteger、AtomicLong、AtomicReferrence及它们的数组类。

二、锁

Lock类:包括Lock和ReadWriteLock两个接口,实现类有ReentrantLock和ReentrantReadWriteLock,后者同时提供共享锁和独占锁。锁提供condition接口,有两个方法await()和signal(),类似于object类的wait()和notify()方法。

LockSupport类:类似于二元信号量(只有1个许可证可供使用),用于阻塞及解除阻塞线程,是创建锁的内部实现,包括park()及unpark()等方法,不可重入,不会出现像Thread类相关方法会出现的死锁现象,内部使用C++的mutex来实现。

Synchronizer类:包括AbstractOwnableSynchronizer、AbstractQueuedSynchronizer、AbstractQueuedLongSynchronizer类,也是创建锁的内部实现。主要用于用户继承来自定义同步类,实现自己的同步逻辑。

Lock的内部使用LockSupport来阻塞线程,使用Synchronizer对等待线程进行排队。

Lock和Synchronized的区别在于:Lock用代码来实现同步,Synchronized是利用JVM来实现同步,其原理为利用对象的Mark Word(标记字段)和线程的Monitor锁来实现,Lock较轻量,执行效率较稳定,但要人为的加锁和解锁,Synchronized在JVM负担不重时,效率更高,但当JVM负担较重时,效率有明显的降低,且由JVM来加解锁,不需要人为控制。

锁都有fair和unfair两种模式。

三、集合类的并发实现

ConcurrentMap:同步时,内部使用segment锁(分段锁)锁定相应索引块。分段锁的设计,只有在同一个分段内才存在竞争关系,不同的分段锁之间没有锁竞争。相比于对整个Map加锁的设计,分段锁大大的提高了高并发环境下的处理能力。

BlockingQueue:使用lock实现线程安全,使用lock.newCondition()的await()/signal()来实现堵塞,当队列已满时阻塞添加对象请求,队列为空时阻塞获取对象请求。它与Queue的区别在于,Queue不阻塞,如果队列已满,添加对象立刻失败返回,如果队列已空,获取对象立刻失败返回。

CopyOnWriteArrayList:读写分离,防止每次访问都要加锁所带来的性能问题,但同时写操作需要大面积复制数组,所以适合读操作远远大于写操作的场景里,比如缓存。

四、线程池

接口关系图:

接口关系图

Executor方法:void execute(Runnable command);

ExecutorService方法: 

    提交单个方法:

        Future submit(Callable task); 

        Future submit(Runnable task, T result);

    提交多个方法:

          List<Future> invokeAll(Collection<Callable> tasks); // 所有线程完成后才返回

         T invokeAny(Collection> tasks); // 任何一个线程完成即返回,同时取消其它任务,内部用BlockingQueue来实现,take+reappend即可。

Runnable和Callable的区别:Callable=Runnable+返回值,callable可以抛出异常到父线程,但runnable不会,Callable通常与Future一起使用。

Thread和Runnable的区别:Runnable是接口,Thread继承于它。

线程池的几个参数及工作原理:

corePoolSize:Worker线程的核心数量。

maximumPoolSize:Worker线程的最大数量。

keepAliveTime:空间线程存活的最长时间。

workQueue:任务队列。

RejectedExecutionHandler:拒绝策略。

1、如果已有线程数少于corePoolSize,创建新的线程来执行新添加的任务。

2、如果已有线程数大于等于corePoolSize,但队列workQueue未满,则将新添加的任务放到workQueue中。

3、如果已有线程数大于等于corePoolSize,且队列workQueue已满,但线程池中的线程数量小于maximumPoolSize,则会创建新的线程来处理被添加的任务。

4、如果线程池中的线程数量等于了maximumPoolSize,就用RejectedExecutionHandler来执行拒绝策略。

ScheduledThreadPoolExecutor:定期开启新线程执行任务,内部使用DelayedWorkQueue存放任务。

五、高并发常用工具类

SynchronousQueue:特殊的BlockingQueue,内部不存储数据,生产者生产消息时,如果没有消费者,会被block,直到一个消费者来取走消息才返回。

Semaphore:本质上可以看成有限的共享锁,即只能被有限数量的线程使用的共享锁。

Exchanger:双向数据传输的SynchronousQueue,即没有生产者和消费者之分,任意两个线程都可以交换数据。Exchanger不存在公平不公平的模式,因为没有排队的情况发生,只要有两个线程就可以发生数据交换。

PriorityBlockingQueue:有序队列,排序的方法可以通过指定Comparator来比较元素的大小,或者元素类型本身实现了Comparable接口。

DelayQueue:延迟队列,即加入队列的元素必须要经过一段设定的时间后才能被取出来。

CyclicBarrier:允许一组线程等待彼此,直到所有线程都ready,然后执行任务。每个线程使用CyclicBarrier.await()来阻塞自己。其实现方式类似于Object.wait()和notifyAll()的使用。

CountDownLatch:类似于CyclicBarrier,但运行方式不一样。两者的区别为:CyclicBarrier是参与的所有线程彼此等待,CountDownLatch则为一个主线程等待,其余每个线程报到一下即可,等到所有线程都报到了,主线程就结束自己的等待。

Fork/Join:Fork分解任务成独立的子任务,用多线程去执行这些子任务,Join合并子任务的结果。这样就能使用多线程的方式来执行一个任务。ForkJoinTask(RecursiveAction或者RecursiveTask实现类)与一般的任务的主要区别在于它需要实现compute方法,在这个方法里,首先需要判断任务是否足够小,如果足够小就直接执行任务。如果不足够小,就必须分割成两个子任务,每个子任务在调用fork方法时,又会进入compute方法,看看当前子任务是否需要继续分割成子任务,如果不需要继续分割,则执行当前子任务并返回结果。使用join方法会等待子任务执行完并得到其结果。

Phaser:阶段器,用来解决控制多个线程分阶段共同完成任务的情景问题。其作用类似于CountDownLatch和CyclicBarrier,但更加灵活,特别是增加了阶段的功能。

上一篇下一篇

猜你喜欢

热点阅读