线程安全:
- 原子性:一个操作要么都执行要么都不执行。
- 可见性:当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。
- 有序性:程序代码按照指令顺序执行。
CAS
- Compare And Swap 比较并交换
- 内存地址的值V,旧的预期的值A,要修改的新值B。只要当V与A相等时,才会将V修改为新的值B。
- Unsafe将cas编译成一条cpu指令,没有函数调用
- aba问题:当前值可能是变为b后再变为a,此a非彼a,通过加版本号能解决
- 非阻塞同步:没有挂起唤醒操作,多个线程同时修改一个共享变量时,只有一个线程会成功,其余失败,它们可以选择轮询。
线程状态:
New(新建)
- 新建,一个线程还没开始执行的状态。
- 新创建的线程,还没有开始执行,也就是还没有调用strat()方法。
Runnable(可运行)
- 正在运行,一个线程在Java虚拟机中正在运行的状态。
- 正在运行的状态不等于CPU正在运行这个线程的代码,也可能正在等待操作系统的资源,比如cpu资源。 当cpu空闲下来时,将会选择一个处于 RUNNABLE 状态的线程执行。
Blocking(阻塞)
- 阻塞,线程阻塞表示正在等待获取监控锁时的状态。
- 此时线程正在等待获取监控锁,以进入一个同步代码块或同步方法,或是在调用了Object.wait() 方法后重新进入一个同步代码块或同步方法。
TimeWaiting(限期等待)
限时等待,线程处于有限时间的等待状态,直到另一个线程执行特定的操作或到时间,将会脱离此状态。线程处于限时等待状态,是由于线程调用了一下方法之一,并且指定一个正数等待时间。
- Thread.sleep()
- 带超时时间参数的 Object.wait()
- 带超时时间参数的 Thread.join()
- LockSupport.parkNanos
- LockSupport.parkUntil
Waiting(无限期等待)
等待,线程处于无限期的等待状态,直到另一个线程执行特定的操作才能摆脱此状态。线程处于等待状态,是由于线程调用了以下方法之一。
- 不带超时时间参数的 Object.wait()
- 不带超时时间参数的 Thread.join()
- LockSupport.park() 线程处于等待状态时,正在等待另一个线程执行特定操作。
Terminated(终止)
终止,已经退出了的线程在此状态。终止状态,表示线程已经执行完毕了。
ReentrantLock和synchronized的区别
- 等待可中断:ReentrantLock当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。
- 公平锁:ReentrantLock默认情况下也是非公平的,但可以通过带布尔值true的构造函数改用公平锁。
- 锁绑定多个条件:一个ReentrantLock对象可以通过多次调用newCondition()同时绑定多个Condition对象。
synchronized和volatile的区别
- synchronized能保证操作的原子性,而volatile不可以,假设线程A和线程B同时读取到变量a值,A修改a后将值更新到主内存,同时B也修改a值会覆盖A的修改操作
- synchronized可修饰变量、方法和类,而volatile只能修饰变量
- synchronized可能会造成线程阻塞,而volatile不会造成线程的阻塞
线程池
- 复用线程池中的线程,避免线程的创建和销毁带来的性能消耗;
- 有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致阻塞现象;
- 进行线程管理,提供定时 / 循环间隔执行等功能
线程池重要参数
- 核心线程数(coolPoolSize): 即使处于空闲状态,也会被保留下来的线程,会一直存活。
- 最大线程数(maximumPoolSize): 核心线程数 + 非核心线程数。控制可以创建的线程的数量。
- 线程存活保持时间(keepAliveTime):非核心线程超时时间,超过这个时长,闲置的非核心线程就会被回收。
- 任务队列(workQueue):用于传输和保存等待执行任务的阻塞队列。
- 线程工厂(threadFactory):可创建新线程。
- 线程饱和策略(handler):当线程池和队列都满了,再加入线程会执行此策略。
死锁
四个必要条件
- 互斥访问资源
- 资源只能主动释放,不会被剥夺
- 持有资源并且还请求资源
- 循环等待 解决方案是:加锁顺序+超时放弃