多线程基础

线程基本概念

-

  • 线程:在一个程序内,程序运行的最小单位,一个程序可以同时执行多个线程

  • 创建线程:
    • 继承 Thread 并重写run方法
    • 实现 Runable 接口,重写run方法
  • 启动线程的三种方式:
    • Thread
    • Runable
    • 线程池 Executors.newCacheThread
  • 让出线程
    • sleep 未运行,先让别的线程先运行
    • yield 运行中让出 ,进入等待队列
    • join 用于等待另外一个线程结束,比如AB线程,A在运行中,通过join将B加入到A线程中,等B运行完成后,再运行A
      • 可以通过join的方式,让线程顺序执行
  • 线程状态

    img

-

  • 中段线程(工程中和少使用)
    • 通过 interrupt() 方法中段 (需要配合try catch使用,)
    • 通过stop() 中段(不建议使用,因为可能数据不一致的问题)
    • 通过shell命令 kill -9 对应线程中段
  • 获取线程状态

    • Thread.getState();
  • 多线程使用场景

    -

  • synchronized 锁定的是某个对象,
    • 对方法加锁
    • 对 对象加锁
    • 锁升级的概念
    • 可重入锁
    • 异常会释放锁,这个很危险,释放锁之后会被其他线程拿到锁做相应的业务处理
    • 八锁
    • Atomic lock 使用的是自旋锁 ,性能比synchronized 要高
      • 自旋锁锁使用场景:执行时间短,线程少(因为等待情况下才会自旋遍历,很消耗cpu资源)
      • 系统锁(synchronized)使用场景:执行时间长,线程多
    • synchronized(object)
      • 不能使用String常量,Integer Long
    • 偏向锁
      • 当线程来的时候,先拿到线程id,先不给对象加锁,当还是之前的线程,那直接执行相应的任务,这样在不直接加锁的情况下,效率更高
      • 当进来的是另外一个线程,那么按照加锁的方式处理
    • 自旋锁
      • 当对象被一个线程锁定后,另一个线程会一直询问,是否可以拿到这把锁,会一直问10次,拿不到就进入wait 状态,进入等待队列
    • 锁升级
      • 偏向锁–>自旋锁–>重量级锁(系统锁)
  • volatile
    • 保证可见效
    • 禁止指令重排
    • 可以保证可见性,但是不能保证原子性,保证原子性 需要使用锁
    • 没有把握不要用
    • 要的时候尽量使用基础变量,不要使用引用值
    • volatile可以保证引用不变,但是引用里面的值观察不到变化
  • 锁优化
    • 锁的粒度细化
    • 锁太多的情况下 多锁做粗化
  • synchronized 锁对象和锁class
    • 1 无论是修饰方法还是修饰代码块都是 对象锁,当一个线程访问一个带synchronized方法时,由于对象锁的存在,所有加synchronized的方法都不能被访问(前提是在多个线程调用的是同一个对象实例中的方法)
    • 2 无论是修饰静态方法还是锁定某个对象,都是 类锁.一个class其中的静态方法和静态变量在内存中只会加载和初始化一份,所以,一旦一个静态的方法被申明为synchronized,此类的所有的实例化对象在调用该方法时,共用同一把锁,称之为类锁。
  • CAS (无锁优化 自旋)
    • Compare and set
    • cas
      • CPU原语支持
    • ABA问题
      • 加version
  • 乐观锁 悲观锁 偏向锁 自旋锁
  • lock 比synchronized 好的的地方
    • lock 用的是cas
    • tryLock 尝试获取锁 boolean
    • 可以打断线程
    • 公平锁 队列里面的线程 按照顺序 线程挨个执行
    • 非公平锁 队列里面等待的线程 随机执行
  • CountDownLatch
    • 用法
  • CyclicBarrier
    • 用法
  • Phaser
    • 用法
  • ReadWriteLock
    • 共享锁
    • 排他锁
    • 怎么用
  • 分布式锁
    • redis
    • zookpeer
  • semaphore
  • LockSupport
    • 可以指定线程结束阻塞状态
  • 核心概念
- 所有的线程中,都是通过队列 AbstractQueuedSynchronizer 来排序
- 线程之间不可见
- notify 不释放锁
- wait 会释放锁,并进入阻塞状态
  • 乐观锁 悲观锁 自旋锁 读写锁(共享锁,排他锁),分段锁 以及在java中的实现方式 分别对应 CAS syn CAS ReadWriteLock LongAdder
  • synchronized 和 Condition 本质区别
    • synchronized 只有一个等待队列,Condition 可以有多个
  • Varhandle
    • 可以将非原子性操作改成原子性操作(普通属性也可以改成原子性操作)
    • 比反射的效率高,Varhandle 可以直接操作二进制的码
  • ThreadLocal
    • set
      • Thread.currentThread.map(ThreadLocal,new persion)
    • ThreadLocal 用途
      • 声明式事务
    • 使用ThreadLocal 不用的时候 ,需要做 remove 不然有可能会出现内存泄漏

容器

vector hashtable

  • ​ 自带锁,性能不好,基本不用

list

set

Queue

  • 高并发
  • 生产者消费者模型
队列    
LinkedBlockingQueue 无界的 最大jvm内存大小
天生的实现了生产者消费者模型
ArrayBlockingQueue 有界的 需要设置队列的大小
PriorityQueue 优先队列 数据按照一定顺序输出,默认又小到大输出,也可以自定义比较输出顺序
DelayQueue 可以用在时间上的排序 按照时间的顺序制作任务
SynchronousQueue 给线程之间传递任务(容量为零) 1. 这里只能用 put 方法 ,只能put一次 ,也不能使用 add 方法
2. SynchronousQueue 没有容量,在put之后进入阻塞状态, 需要把数据tack出去
LinkedTransferQueue 可以在线程间传递多个任务 1. transfer() 是阻塞方法,需要 tack() 后才解除阻塞,所有需要先执行 tack() 方法
2.相对 SynchronousQueue 只能两个线程之间信息传递 ,TransferQueue 可以多个线程拿数据
3. 相对 SynchronousQueue 容量为零, TransferQueue 可以在队列里面插入多条数据
ConcurrentLinkedQueue 双端队列Deque 1. add() 添加不了抛出异常
2. offer 能不能呢添加会有一个boolean的返回值
3. poll() 取 会remove // peek() 取 不会remove

Map

三个map对比 写入 读取
Hashtable 相对快
Collections.synchronizedMap(new HashMap<UUID, UUID>()); 相对快
ConcurrentHashMap 相对慢一点 极快
  • linkedhashmap 在hashmap的基础上增加了linked,加快了遍历的速度,但是在查找的时候比较麻烦,它的查找时间负责度是O(n),

  • treeMap 排好顺序,在查找的时候效率比较高,插入的时候效率也没那么低
  • ConcurrentSkipListMap
    • 在多线程高并发的情况下,有ConcurrentSkipListMap 替代 treemap
    • 高并发并且排序
    • 跳表的概念,(在查找的时候有点二分查找的感觉)

ConcurrentSkipListMap

  • CopyOnWriteList 写时复制
普通容器 并发容器 并发容器使用场景
ArrayList CopyOnWriteArrayList 读多写少(写加锁,读不用加锁)
HashMap ConcurrentHashMap 并发时替代HashMap
TreeMap ConcurrentSkipListMap 高并发并且排序
ArrayList Collections.synchronizedList(new ArrayList)  
     
  • list 和 Queue 却别
    • Queue提供了 offer peek poll 等 对线程友好的api
    • BlockingQueue 提供了 put 和tack阻塞方法 ,是一个很好的 生产者消费者模型

线程池 ThreadPool

Callable –> Runable + return

Future:接受未来将会产生的结果

FutrueTask –> Futurn+Runable

ExcuteThreadPool 7大参数

ExcuteThreadPool 7大参数  
coorePoolSize 核心线程
maximumPoolSize 最大线程数
线程空闲时多久销毁 可以设置任何时间长度
销毁的时间单位 对应 秒,微秒等
任务队列 BlockingQueue 的子类都可以使用
生产线程的方式 jdk提供了一种默认策略,也可以自定义
执行任务的线程和队列满了后,对于的处理方式 jdk提供了四种策略,也可自定义处理策略

ExcuteThreadPool

  • 所有线程共用同一个队列
  1. 为甚么有SingleExcuteThread

    任务队列,线程管理

    1. Cached vs Fixed

    阿里都不用,自己估计,进行精确定义

    1. Scheduled 定时任务线程池

    定时器框架:quartz

    面试题:假如提供一个闹钟服务,订阅的人有10亿,怎么优化?

并发和并行的区别

并发是指任务提交,并行指任务执行

ForkJoinPool

  • 每个线程有自己的队列
  • 使用大的任务,通过切分成小的任务加快运行速度