本文最后更新于 493 天前,其中的信息可能已经有所发展或是发生改变。
一、线程
1. 线程方法
- new T1().run() 调用run方法,同步的
- Thread.yield() 让一下CPU,线程进入等待队列,从RUNNING变为RUNABLE状态
- t.join() 等待t线程运行结束再运行
2. 线程状态
注意:Wating的原因
二、关键字
1. volatile
- 保证线程可见性
- MESI
- 缓存一致性协议
- 禁止指令重排序
- loadfence原语指令(读屏障)
- storefence原语指令(写屏障)
- DCL单例(Double Check Lock)
- DCL指令重排序可能造成的问题
- new对象的过程
- 分配内存(数据为默认值)
- 初始化数据
- 引用变量指向对象的内存地址
- 指令重排序后,上诉步骤可能变成1-3-2,执行完3时,假如另一个线程来获取实例,通过Double Check的第一个Check检查时,发现引用变量不为NULL(此时引用变量指向的是刚分配内存但未初始化数据的对象),便拿去使用了,造成事故
- new对象的过程
- 单例模式,饥汉式的缺点,程序一启动就创建对象,占内存。饿汉式是需要时再创建对象。
三、锁
1. synchronized
- Hotspot:synchronized通过对象头里的两位(总共64位)来标记的
- Synchronized加到非静态方法上等效于,synchronized(this){}
- Synchronized加到静态方法上等效于,synchronized(T.class){}
- synchronized异常会释放锁,可能造成数据异常(其他线程拿到的数据可能是没处理完的)
- synchronized的底层实现 jdk早期:重量级-os 后来改进:锁升级(只升不降) 没错,我就是厕所所长!
- 坑位偏向锁一把,先到先得,本所小线程负责贴标签(记录线程id)
- 人少请自旋,其他线程为RUNABLE状态,占cpu,但不访问操作系统,在用户态解决,不经过内核态
- 打转儿的次数够多(JDK1.6规定为10次),或者打转儿的人够多(JDK目前规定为自旋线程超过CPU内核数的一半),我们小线程们负责找重量级老大申请重量级锁-os,其他线程为WATTING状态,不占cpu,适合临界区执行时间长的场景
- synchronized(Object)不能锁String常量、Integer、Long,因为可能在不同的地方对同一个对象上了锁
- 不阻止指令重排序
- 锁的颗粒度尽量小
- 锁对象时,尽量使用final来定义引用变量,避免使用过程中,引用变量指向其他对象,造成线程不同,同一个锁,锁的对象不同,引发事故
2. CAS
Compare And Set,无锁优化,自旋
CPU原语支持,运行期间不被打断
代码语言:javascript复制cas(V,Expected,NewValue)
if V==E
V=New
otherwise
try again or fail
ABA问题
线程1读取的共享变量为A,进行CAS操作,期间其他线程将该变量修改为B后,又修改为A。CAS认为此变量符合预期。
解决方式:加版本号
java.util.concurrent.atomic包下都是CAS原理
atomicLong在并发量较低的环境下,线程冲突的概率比较小,自旋的次数不会很多。但是,高并发环境下,N个线程同时进行自旋操作,会出现大量失败并不断自旋的情况,此时AtomicLong的自旋会成为瓶颈。
AtomicLong中有个内部变量value保存着实际的long值,所有的操作都是针对该变量进行。也就是说,高并发环境下,value变量其实是一个热点,也就是N个线程竞争一个热点。
LongAdder的基本思路就是分散热点,将value值分散到一个数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回。
这种做法有没有似曾相识的感觉?没错,ConcurrentHashMap中的“分段锁”其实就是类似的思路。
3. ReentrantLock
需要手动lock和unlock,一般将unlock写到finally里。synchronized遇到异常会自动释放锁,而ReentrantLock不会。
底层为CAS加等待队列
trylock:尝试获取锁
代码语言:javascript复制boolean locked = false;
try {
locked=lock.tryLock(time:5,TimeUnit.SECONDS);
System.out.println("m2..." Locked);
} catch(InterruptedException e){
e.printStackTrace();
} finally {
if(locked) lock. unlock();
}
lockInterruptibly允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException。
公平锁
参数为true表示为公平锁,请求锁的线程在等待队列里FIFO
代码语言:javascript复制ReentrantLock lock=new ReentrantLock(true);
4. CountDownLatch
代码语言:javascript复制CountDownLatch countDownLatch = new CountDownLatch;
countDownLatch.countDown();
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}