zoukankan      html  css  js  c++  java
  • 多线程-JUC

    1、volatile关键字与内存可见性

    volatile关键字:当多个线程操作共享数据时,可以保证内存中数据可见性。效率比synchronized快。
    但是使用volatile修饰后JVM优化不能重排序。相较于synchronized是一种轻量级的同步策略。
    注意:
      1、volatile不具备“互斥性”
      2、volatile不能保证变量的“原子性”。

    2、原子性
    JDK 1.5之后,Java提供了原子变量,在java.util.concurrent.atomic包下。原子变量具备如下特点:
      有volatile保证内存可见性。
      CAS(Compare-And-Swap)算法保证数据变量的原子性。

    CAS算法:
    CAS算法是计算机硬件对并发操作共享数据的支持,CAS包含3个操作数:

    内存值V
    预估值A
    更新值B
    当且仅当V==A时,才会把B的值赋给V,即V = B,否则不做任何操作。

    3、锁分段机制
    JDK 1.5之后,在java.util.concurrent包中提供了多种并发容器类来改进同步容器类的性能。其中最主要的就是ConcurrentHashMap。
    java.util.concurrent包还提供了设计用于多线程上下文中的 Collection 实现: ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、CopyOnWriteArrayList 和 CopyOnWriteArraySet。当期望许多线程访问一个给定 collection 时,ConcurrentHashMap 通常优于同步的 HashMap, ConcurrentSkipListMap 通常优于同步的 TreeMap。当期望的读数和遍历远远 大于列表的更新数时,CopyOnWriteArrayList 优于同步的 ArrayList。 

    4、闭锁
    CountDownLatch :闭锁,在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才继续执行。

    CountDownLatch是JAVA提供在java.util.concurrent包下的一个辅助类,可以把它看成是一个计数器,
    其内部维护着一个count计数,只不过对这个计数器的操作都是原子操作,同时只能有一个线程去操作这个计数器,
    CountDownLatch通过构造函数传入一个初始计数值,调用者可以通过调用CounDownLatch对象的countDown()方法,来使计数减1;
    如果调用对象上的await()方法,那么调用者就会一直阻塞在这里,直到别人通过countDown方法,将计数减到0,才可以继续执行。

     CountDownLatch 底层是基于 AQS(AbstractQueuedSynchronizer)实现的.

    5、创建线程的方式-实现Callable接口

    相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
    执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。 FutureTask 是 Future 接口的实现类
    现在Callable接口和实现Runable接口的区别就是,Callable带泛型,其call方法有返回值。使用的时候,需要用FutureTask来接收返回值。
    而且它也要等到线程执行完调用get方法才会执行,也可以用于闭锁操作。

     

    6、Lock同步锁
    在JDK1.5之前,解决多线程安全问题有两种方式(sychronized隐式锁):

    同步代码块
    同步方法
    在JDK1.5之后,
    出现了更加灵活的方式(Lock显式锁):

    同步锁
    Lock需要通过lock()方法上锁,通过unlock()方法释放锁。为了保证锁能释放,
    所有unlock方法一般放在finally中去执行。

    等待唤醒机制
    用Lock锁实现等待唤醒:需要创建lock对象和condition实例。condition的await()方法、signal()方法和signalAll()方法分别与wait()方法、notify()方法和notifyAll()方法对应。

    线程按序交替
    创建三个线程,分别调用loopA、loopB和loopC方法,这三个线程使用condition进行通信。


    ReadWriterLock读写锁
    我们在读数据的时候,可以多个线程同时读,不会出现问题,但是写数据的时候,如果多个线程同时写数据,那么到底是写入哪个线程的数据呢?所以,如果有两个线程,写写/读写需要互斥,读读不需要互斥。这个时候可以用读写锁

    7、线程池
    线程池用法很简单,分为三步。首先用工具类Executors创建线程池,然后给线程池分配任务,最后关闭线程池就行了。

    * 二、线程池的体系结构:
    * java.util.concurrent.Executor : 负责线程的使用与调度的根接口
    * |--**ExecutorService 子接口: 线程池的主要接口
    * |--ThreadPoolExecutor 线程池的实现类
    * |--ScheduledExecutorService 子接口:负责线程的调度
    * |--ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService
    *
    * 三、工具类 : Executors
    * ExecutorService newFixedThreadPool() : 创建固定大小的线程池
    * ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
    * ExecutorService newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程
    *
    * ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。
    */

    8、线程八锁:
    非静态方法的锁默认为 this, 静态方法的锁为 对应的 Class 实例
    某一个时刻内,只能有一个线程持有锁,无论几个方法。

    9、ForkJoinPool
    工作窃取模式,ForkJoinPool的优势在于,可以充分利用多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。这种思想值得学习。

  • 相关阅读:
    点对点模式1
    简要分析《XXX需求征集系统》采用的可用性和可修改性战术
    淘宝为例进行软件质量属性分析
    浅谈软件架构师的工作
    《软件需求十步走》阅读笔记6
    编写有效的业务用例 读书笔记03
    编写有效的业务用例 读书笔记02
    编写有效的业务用例 读书笔记01
    2017年秋季个人阅读计划
    软件需求与分析需要掌握的内容
  • 原文地址:https://www.cnblogs.com/spdboke/p/14582065.html
Copyright © 2011-2022 走看看