线程启动的三种方式
1. new Thread().start()
2.new Runnable().start();
3.使用线程池。Executors.newCachedThreadPool()
线程的六种状态
1.new 新建一个线程,未启动。
2.runnable 已经start的线程,等待资源,比如cpu资源。
3.blocked 阻塞状态。等待进入同步代码块锁
4.waiting 。调用了wait(),join(),LockSupport.park() 。 等待重新被唤醒。
5.TIMED_WAITING :sleep、wait(time)、LockSupport.parkUntil(1000)、LockSupport.parkNanos(10000) 知道某个时刻被唤醒。
6.TERMINATED 死亡
synchronized 和 CAS锁
cas自旋锁比较占用cpu时间,synchronized不占用cpu,使用的是wait队列。
线程数少使用自旋,否则使用synchronized
synchronized 锁定的是对象而不是代码。重量级锁,直接调用的汇编语言的 lock 命令。
AtomicXXX的类都是使用CAS锁
CAS 锁 : compareAndSwap等等,都是CPU支持的。
使用CAS锁会存在ABA问题。ABA即,比如甲对丙说我给你买了一瓶农夫山泉,让乙带给你了。路上乙渴了然后喝掉了,去便利店买了一瓶农夫山泉给了丙。农夫山泉还是农夫山泉,但是已经不是甲给的那瓶。
解决ABA问题:加版本号。如果是int类型,基础类型的没什么关系。
AQS 底层就是CAS+ volatile 。关于AQS
volatile 可见性、禁止指令重排,但是不能保证原子性
volatile 通过 MESI (intel cpu协议) 缓存一致性协议 保证多线程可见性。
public class ThreadTest {
static volatile ThreadTest instance;
ThreadTest(){
}
static ThreadTest getInstance(){
if(instance == null){
synchronized (ThreadTest.class){
if(instance == null){
instance = new ThreadTest();
}
}
}
return instance;
}
如上单例代码 static volatile ThreadTest instance; 是否需要使用volatile。
必须! 因为 instance = new ThreadTest(); 非原子性操作。1.先向内存申请一片 ThreadTest 对象空间 2.对该对象成员变量初始化3.将这块内存赋值给 instance 。 volatile 可以禁止 1、2、3 指令重排。
hotspot中正式利用了这一点来区分无锁与偏向锁。无锁与偏向锁的锁状态都为01,但是无锁的biased_lock:表示为0,偏向锁表示为1这就很好的区分了无锁与偏向锁。
无锁------------001
偏向锁-----