美文网首页
Java编程学习必备:最强并发编程学习大纲,简洁,但不失重点!

Java编程学习必备:最强并发编程学习大纲,简洁,但不失重点!

作者: Java_苏先生 | 来源:发表于2020-02-12 17:55 被阅读0次

    一、多线程

    1. 无返回值

    ①. 实现runnable接口

    • run方法不会抛异常
    • 需要Thread的start方法启动多线程

    ②. 继承Thread类

    • java只能单继承,所以扩展收到限制

    2. 有返回值(实现Callable接口)

    ①. future拿 到返回值

    • 拿返回值
    • 判断任务是否执行完
    • 中断任务
    • 向线程池summit的多个任务,只有全部执行完,future才可以get到值

    ②. call方法会抛异常

    ③. 需要Thread的start方法启动多线程

    3. 解决future的get方法阻塞问题completionService

    ①. take方法也是阻塞方法,只是会拿其中一个完成的future

    ②. poll方法和take类似,只是poll方法不会阻塞,没有完成的任务直接返回null,可以加等待的时间

    4. ThreadLocal

    ①. 线程间的数据隔离

    ②. 解决多线程安全问题

    • set(往里面放数据
    • get(从里面取当前线程的数据
    • 使用完get和set后要用remove去除内部map的key与value的引用关系,因为key是弱引用,下次gc的时候被回收,导致value会被线程长期持有,造成内存泄露

    ③. 优雅做法:帮ThreadLocal包装到单例中

    二、并发包

    • BlockingQueue
    • ConcurrentHashMap
    • ReentrantLock
    • LockSupport
    • CyclicBarrier
    • CountDownL atch
    • ReadWriteLock
    • Semaphore
    • Condition

    三、相关问题理解

    1. 对volatile的理解

    ①. 虚拟机提供的轻量的同步机制

    • 保证可见性:前后加了内存屏障
    • 不保证原子性
    • 禁止指令重排:当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行

    ②. 在JMM的理解

    • 可见性:主内存,线程工作内存
    • 原子性
    • 有序性质:指令重排

    ③. 哪些地方用过volatile

    • 单例模式的double check (禁止 了指令重排)
      • 在真正的new操作对象的时候,new命令其实包含了,开辟内存,初始化内存等操作:
      • 多线程情况下,一个线程在new的时候,内存还没有被初始化完全,另一个线程进来后发现对象引用已经不是null了,就会返回一个未初始化完全的对象,从而造成出错
    • 代理模式

    2. CAS的理解(比较交换)

    ①. 工作内存与主存数据比较一样的时候修改,不一样的时候重复读取主存,跟新工作内存数据

    ②. CAS底层,unsafe的理解:native方法,unsafe类

    ③. CAS缺点:

    A. 循环时间长,CPU开销大

    B.只能保证一个共享变量的原子性:不能保证代码块的原子性

    C. 有ABA问题:

    • 原因:一个线程短时间内把变量由A改成B,再由B改成A
    • 解决:加入时间戳版本,原子引用
    • AtomicStampedReference

    3. Arayit是线程不安全的,解决线程不安全的方言

    • 加锁
    • 使用vector线程安全的数组
    • 使用Collections内部方法转为线程安全的List

    4. 各种锁的理解

    ①. 公平锁/非公平锁

    • 是什么:公平锁只等待锁的线程按先来先得顺序得到锁
    • 两者区别:公平锁按FIFO的等待队列等待锁, 锁操作耗时
    • ReentrantLock默认是非公平锁,synchronize是非公平锁

    ②. 对象锁

    • Synchronize.每 个对象的头部markword字段有标志标志,monitorenter 和monitorexit 指令来实现同步的,monitor管程 来控制
    • 头部mark word字段,有锁状态,是否偏向锁,锁标志
    • JVM级的不需要业务代码控制

    ③. 偏向锁

    • 偏向锁的核心思想是,如果一个线程获得 了锁,那么锁就进入偏向模式,此时Mark Word的结构也变为偏向锁结构,当这个线程再次请求锁时,无需再做任何同步操作,即获取锁的过程,这样就省去了大量有关锁申请的操作

    ④. 可重入锁

    • 是什么:同一个线程外层函数获取锁后,内存递归函数仍然能获得锁
    • ReentrantLock和Snychronize是典型的可重入锁
    • 底层实现原理,ReentrantLock是AQS同步队列, Snychronize是锁住的对象头部字段记录的标志,在monitor中 会做计数

    ⑤. 自旋锁

    • SpinLock,短时间的自旋,不会释放CPU

    ⑥. 独占锁(写)/共享锁(读)

    • ReentrantReadWriteLock

    ⑦. 读写锁

    • 写的时候排他,读的时候共享排他写锁

    ⑧. 锁优化

    • 偏向税→轻量级锁→自旋锁→重量级锁

    5. CountDownLatch/CyclicBarrier/Semaphore

    ①. CountDownLatch

    • 让一些线程阻塞,知道一些线程完成操作
    • 一些线程执行await(阻塞,一些线程完成操作后执行CountDownQ减数
    • 案例:等待人到齐,才开会

    ②. CyclicBarrier

    • 一些线程阻塞在一个点, 然后同时进行
    • await(方法阻塞
    • 案例:跑步比赛到齐开始

    ③. Semaphore

    • 用于并大资源数量的控制,同-时间只能有固定数的线程进入临界区
    • 案例:抢车位

    6. 阻塞队列

    ①. 是什么

    • 阻塞队列空的时候,从队列获取数据的线程会被阻塞
    • 阻塞队列满的时候,往队列放数据的线程会被阻塞

    ②. 好处

    • 不需要关心什么时候阻塞线程,什么时候唤醒,阻塞队列帮处理了

    ③. Blockqueue核心方法处理出错

    • 抛异常
    • 返回特殊值
    • 一直阻塞
    • 超时退出

    ④. 架构种类

    • ArrayBlockingQueue基于数组的有界
    • LinkedBlockingDeque基于链表的有界,默认界值很大
    • SynchronousQueue同步队列,不存数据,生产一个消费一 个
    • PriorityBlockingQueue有优先级的无界阻塞队列
    • delayQueue延迟无界阻塞队列
    • LinkedTransferQueue链表结构的无界阻塞队列
    • LinkedBlockingDeque链表结构阻塞双端队列

    ⑤. 用在哪里

    • 生产者消费者模式
    • 线程池
    • 消息中间件

    7. Java线程池,ThreadPoolExecutor的理解 田

    ①. 线程池优势

    • 充分利用CPU
    • 减少频繁创建线程的性能消耗

    ②. 线程池的使用

    • 自定义线程池,继承ThreadPoolExecutor
    • 线程池函数
      • Executors.newCachedThreadPool
      • Executors.newSingle ThreadPool
      • Executors.newFixedThreadPool
      • Executors.newScheduleThreadPool

    ③. 线程池的重要参数

    • corePoolSize线程池的常驻核心线程数
    • maximumpoolSize线程池能够容纳的最多线程数
    • keepAliveTime多余核心线程数的空闲线程的最多存活时间
    • unit keepAlive Time的单位
    • workqueue任务队列,提交到线程池还未处理的任务
    • threadFactory线程池中线程的工厂。用于创建线程,
    • handler拒绝策略,任务丢列满,线程数达到最大时,做的处理

    ④. 线程池的工作原理

    a. 创建线程池后等待提交任务

    b. 调用execute添加任务,线程池做的判断:

    • 如果线程数量小于核心线程数,就马上创建线程处理任务
    • 如果正在运行的线程数大于核心线程数,就将任务放入任务队列
    • 如果任务队列满了,且正在运行的线程数小于最大线程数,就创建非核心线程处理任务
    • 如果队列满了,且线程数达到最大线程数,线程池使用饱和拒绝策略处理任务

    c. 当一个线程任务处理完,会从任务队列中取下一个任务执行

    d. 当一个线程空闲时间超过keepAliveTime时候,线程池处理(如果当前线程数量大于核心线程数,那么这个线程就会被销毁)

    8. 线程池参数的合理配置

    ①. 线程池拒绝策略

    • AbortPoliy直接抛出异ReiectedException.阻止系统运行
    • CallerRunPoliy由提交任务的业务线程处理_
    • DisadndeltPole她弃队列中等待最久的任务,然后把最新的任务加入人任务队列
    • DicardPolil 直接丢弃任务,不予处理。也不抛出异常,允许任务丢弃

    ②. 创建线程池的方法

    a. 使用自定义线程池

    b. executors线程池问题:

    • 默认使用链表有界阻塞队列,界很大,导致内存溢出
    • 缓存调度线程池会创建大量线程导致资源占用大

    ③. 如何合理配置线程池数量

    • CPU密集型操作: cpu核心数+1
    • IO密集型操作: 2xcpu核心数

    9. 死锁定位分析

    ①. 产生原因:

    • 系统资源不足
    • 资源分配不当
    • 线程执行顺序不合适
    • 线程互斥,资源持有等待,不可剥夺

    ②. 解决:jps定位到进程id, jstack定位到栈代码

    # 链接 Java程序员福利"常用资料分享"

    相关文章

      网友评论

          本文标题:Java编程学习必备:最强并发编程学习大纲,简洁,但不失重点!

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