马士兵JUC
2020-05-25 14:09:05 16 举报
AI智能生成
JUC课程学习笔记
作者其他创作
大纲/内容
基础
起线程的三种方式(吹毛求疵的面试题)
new Thread
Runnable
Executors.newCachedThread
Thread.yield()
让出一下CPU
new Thread(runable).join()
把cpu让给该线程,等该线程执行完了再继续往下运行
线程状态图
jvm每起一个线程,os都会起一个线程与它对应,每起一个线程的消耗大概是1M,切换线程时也得经过内核态切换
自旋锁和系统锁的选择?
自旋锁消耗cpu但不经过内核态
因此如果执行时间段(加锁代码少),线程数少,则用自旋
执行时间长,线程数多,用系统锁
synchronized
锁机制
synchronized(obj)把一个对象当成锁,通过该对象的对象头数据来判断是否为该进程
对象头上有两个标记位,用于记录当前的锁(锁升级)
第一个线程访问时:偏向锁
单纯记录线程号(其实没加锁)
有线程争用时,升级为自旋锁
等待的线程先进行自旋(默认自旋10次)用户态
阻塞线程自旋次数达标后还拿不到锁时,升级为重量级锁,该进程进入等待状态(wait)
重量级锁需要找os申请(经过内核态)
对象头还记录当前持有锁的线程,以及进入锁次数(可重入锁的实现)
加锁对象不能是String Integer Long等基础数据类型
程序中如果抛出异常,锁默认会被释放
volatile
保证线程可见性
每个线程都有自己的工作内存,会把使用到的值从共享内存复制到工作内存,线程对工作内存变量的修改不会马上同步到共享内存。加上volatile可保证每次都同步数据到主存与其他线程,各线程之间缓存一致
实现依赖硬件的MESI协议(缓存一致性协议)
禁止指令重排序
什么是指令重排序?
CPU执行指令可并发执行多条,为了充分利用这一点,编译器对源码进行编译完后,可对指令进行重新排序
实现原理,cpu级别加了读屏障和写屏障
loadfence原语指令
storefence原语指令
mfence
面试例子,单例双重检查:
DCL单例(懒加载)
new对象分为3个步骤
1.申请内存空间
2.成员变量初始化
3.变量地址指向该内存空间
new过程中发生指令重排序(第二步和第三步就可能发生重排序)
CAS(无锁优化 自旋 乐观锁)
采用该技术的工具:AtomicXXX类,Reentranlock等
longadder
内部用了分片锁的方式,用的也是CAS操作
atomicXXX
synchronized
上面三种方式,计数时,atomic效率要比synchronized效率要高。在并发线程,循环比较多时,longadder效率会比atomic高
采用CPU级别原语指令实现,java中采用Unsafe类
cas(V,Expected,NewValue)
V:要更改的对象
Expected:期望当前的值
NewValue:要更改的新值
ABA问题
简单讲,一个值0-》1,1-》0,此时另外一个线程使用CAS操作不能发现值被更改过
解释影响:如果是基础类型,无所谓。如果是引用类型,你的女朋友跟你复合,中间经历了别的男人,她还是原来的女友吗
解决办法:加版本号
atomicXXX就有带版本号操作的类
ReentrantLock
注意点
由于不像synchronized那样抛出异常就释放锁,所以一定要在finally里面写lock.unlock
使用方法
new Reentrantlock(fair)
fair参数默认为false,表示为非公平锁
true为公平锁
当一个线程来拿锁时,会先判断队列里是否有线程在等待,如果队列有在等待的,则它也进入等待
非公平锁时,不管队列里面有没线程等待,直接尝试去抢锁
lock()
trylock(time,timeunit)
unlock()
lockInteruptibly()
可以被打断的加锁
condition相关
创建一个condition:lock.newCondition()
condition.signalAll()
condition.await()
其他juc类
CountDownlatch
latch =new CountDownlatch(num)//num为门栓数,
latch.countDown()//计数减一
latch.await() //阻塞,知道计算减为0
与join相比的好处,countDownlatch在一个线程内可以多次减去计数,即一个线程多次执行countDown()
CyclicBarrier
barrier = new CyclicBarrier(parties,runnable)
类似栅栏,拦住线程,拦住线程数量达到了parties后就放行(即释放await的线程)
runable设置的是栅栏放开时的触发事件,可不设置
循环触发,线程数满了就释放一次,然后继续累积
barrier.await()
Phaser
phaser = new MarriagePhaser()
MarriagePhaser是一个自定义的(业务方可随便自定义)、继承Phaser的类
重写boolean onAdvance(phase,registeredParties)
该方法会在阶段目标达成时触发
phase代表当前为第几阶段,从0开始
registeredParties表示当前阶段有多少线程参加
返回值,false表示继续往下执行后续阶段,true表示不再执行后续阶段
phaser.bulkRegister(num);
设置要num线程调用arriveAndAwaitAdvance才sauna该阶段达成
phaser.arriveAndAwaitAdvance()
phaser.arriveAndDeregister()
取消注册,表示退出后续的阶段
阶段锁
ReadWriteLock
readWriteLock = new ReentrantReadWriteLock();
readLock = readWriteLock.readLock();
共享锁
writeLock = readWriteLock.writeLock();
排他锁
***lock.lock()
***lock.unlock()
抛出异常不会释放锁,所以要写在finally块中
Semaphore
s = new Semaphore(num, fair);
num为信号灯个数,fair为是否为公平锁
s.acquire();
获取
s.release();
释放
可用于限流,令牌算法
Exchanger
Exchanger<String> exchanger = new Exchanger<>();
exchanger.exchange(s);
两线程进行数据交换
LockSupport
LockSupport.park();
LockSupport.unpark(t2);
t2为线程参数,即所要唤醒的线程
源码阅读原则
画泳道图,例如左边的reentrantlock
AQS结构
重要组成成分是state和一个线程队列
state的意义由子类自己定义,例如reentrantlock的代表是否被锁住了,以及重入锁次数。而countDownloadlatch则代表当前剩余门栓数
VarHandle作用
比反射快,直接操作二进制码
通过handle可以做CAS操作。。compareAndSet
强软弱虚引用
强引用:只要一对象有强引用,垃圾回收器就不会回收
软引用:如果一个对象只具有软引用,当内存空间不够了,垃圾回收器就会回收它
弱引用:只要垃圾回收器触发回收,只具有弱引用的对象都会被回收
虚引用:必须和引用队列配合使用,当对象被回收时,可通过队列收到通知
0 条评论
下一页