多线程与锁
多线程并发执行任务的过程中,通过锁将并行的任务转为串行任务执行。
锁
synchronized 对象锁
- 自动加锁和解锁
- 锁升级过程(了解jmm java内存布局)
- 无锁 只有一个线程时,标志位0,记录threadId
- 偏向锁 两个线程竞争,标志位1,记录threadId
- 自旋锁 多个线程但竞争不大时,都会先尝试获取锁(默认10次)
- 重量级锁 多个线程竞争锁而且尝试多次获取不到时,进入阻塞队列,依次执行
lock 显式锁
lock/tryLock 获取锁
unlock 释放锁
java.util.concurrent.locks.ReentrantLock 可重入锁
java.util.concurrent.locks.ReentrantReadWriteLock 读写锁
写加锁,读不加锁java.util.concurrent.ConcurrentHashMap
cas (compareAndSwap)
自旋锁、乐观锁、无锁
原子操作(cpu级别保证操作不会发生指令重排)
- java.util.concurrent.atomic.AtomicInteger
sun.misc.Unsafe jdk提供的原子操作方法的接口
- unsafe.compareAndSwapInt(this, valueOffset, expect, update)
ThreadLocal 是把一个变量在线程栈内复制多个副本,空间换时间
锁的类型
乐观锁、悲观锁
乐观锁:每个线程先尝试获取锁,获取不到后进入阻塞队列
悲观锁:直接进入阻塞队列公平锁、非公平锁
公平锁:每个线程都要进入阻塞队列排队后等待cpu调度
非公平锁:抢占执行可重入锁、不可重入锁
可重入锁:一个线程获取到外层锁后还可以获取内层锁
不可重入锁:一个线程获取外层锁后,只有释放后才能获取内层锁偏向锁
行锁、表锁
读写锁
多线程
线程与进程
- cmd java命令,即可启动一个jvm就是一个进程(包含一个main线程)
- main线程可以创建一或多个子线程
- deamon线程下的普通线程执行完后,会自动结束(如gc-deamon线程,会自动结束)
如何创建线程
- 继承Thread类
- 实现Runnable接口
- Callable/Future/CompletableFuture 有返回值
线程的状态与变化
graph LR; New --start--> Runnable --获得cpu时间片--> Running --运行完成--> Dead Running --执行完cpu时间片--> Runnable Running --sleep/锁等待--> Blocked --sleep结束/获得锁--> Runnable
线程的方法
- join 主线程在此处会等待子线程执行完
- sleep 当前线程进入阻塞态,会释放cpu,但不会释放锁
- wait 释放cpu,不释放锁
- yield 释放cpu
- notifyAll 唤醒cpu与持有锁的线程
线程池是什么?线程池的创建?
java.util.concurrent.Executors
- newCachedThreadPool 无限的新线程
- newFixedThreadPool 核心线程数固定,无限排队线程
- newSingleThreadExecutor 只有一个线程运行,其他线程排队
- newScheduledThreadPool 定时执行线程
- newWorkStealingPool 抢占式运行线程
java.util.concurrent.ThreadPoolExecutor
可根据需要定制线程池1
2
3
4
5
6
7public ThreadPoolExecutor(int corePoolSize, 核心(初始化)线程数
int maximumPoolSize, 最大线程数
long keepAliveTime, 存活时间
TimeUnit unit,
BlockingQueue<Runnable> workQueue, 任务队列
ThreadFactory threadFactory, 线程工厂
RejectedExecutionHandler handler) 拒绝策略BlockingQueue 阻塞队列
- ArrayBlockingQueue
- LinkedBlockingQueue
- DelagedWorkQueue
ThreadFactory 线程工厂
RejectedExecutionHandler 拒绝策略
- ThreadPoolExecutor.AbortPolicy
- ThreadPoolExecutor.DiscardPolicy
线程安全
多线程下编程时,导致结果不唯一的处理方式都是不安全的,
多线程时会遇到哪些问题?
- 指令重排 (加锁,原子操作)
- 伪共享 (volatile、ThreadLocal)
- 线程安全
如何停止正在运行的线程?
使用标志位,在线程运行时,定期去检查标志
- jvm stop-the-world
- tomcat stop
线程同步类
通过这些类,可以给多个异步执行的线程设置集合点,在此以后可以同步执行
java.util.concurrent.CountDownLatch 计数器
java.util.concurrent.CyclicBarrier 循环栅栏
java.util.concurrent.Semaphore 信号量