美文网首页
Concurrent包总结

Concurrent包总结

作者: 怒放的生命_浴火重生 | 来源:发表于2018-03-06 14:59 被阅读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,但更加灵活,特别是增加了阶段的功能。

    相关文章

      网友评论

          本文标题:Concurrent包总结

          本文链接:https://www.haomeiwen.com/subject/voyhfftx.html