线程基本概念
-
-
线程:在一个程序内,程序运行的最小单位,一个程序可以同时执行多个线程
- 创建线程:
- 继承 Thread 并重写run方法
- 实现 Runable 接口,重写run方法
- 启动线程的三种方式:
- Thread
- Runable
- 线程池 Executors.newCacheThread
- 让出线程
- sleep 未运行,先让别的线程先运行
- yield 运行中让出 ,进入等待队列
- join 用于等待另外一个线程结束,比如AB线程,A在运行中,通过join将B加入到A线程中,等B运行完成后,再运行A
- 可以通过join的方式,让线程顺序执行
-
线程状态
-
- 中段线程(工程中和少使用)
- 通过 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 不然有可能会出现内存泄漏
- set
容器
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
- 高并发并且排序
- 跳表的概念,(在查找的时候有点二分查找的感觉)
- 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
- 所有线程共用同一个队列
-
为甚么有SingleExcuteThread
任务队列,线程管理
- Cached vs Fixed
阿里都不用,自己估计,进行精确定义
- Scheduled 定时任务线程池
定时器框架:quartz
面试题:假如提供一个闹钟服务,订阅的人有10亿,怎么优化?
并发和并行的区别
并发是指任务提交,并行指任务执行
ForkJoinPool
- 每个线程有自己的队列
- 使用大的任务,通过切分成小的任务加快运行速度
PREVIOUS多线程