请滑走,自用,无营养警告⚠
目录
【自旋锁 优 缺】
线程同步
Synchronized作用范围
线程之间的协作
synchronized和reentranklock的区别
【独占锁】:ReentrantLockSynchronized
【共享锁】:ReentrantReadWriteLock读共享写独占
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
rwLock.writeLock().lock(); rwLock.writeLock().unlock();
rwLock.readLock().lock();rwLock.readLock().unlock();
【公平锁】线程按照申请锁的顺序来获取锁,先来先服务。如果等待队列不为空,就加入队列。
【非公平锁】获取锁不按照申请锁的顺序,上来就直接尝试占有锁。
优:吞吐量大,性能高。发出请求时,如果锁被其他线程持有,新的线程将被放入到队列中被挂起,但发出请求时恰好锁被释放了,跳过队列中所有的等待线程而获得锁。恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟(相当于人被叫醒需要时间)(新线程C很可能会在队列里的B被完全唤醒之前获得、使用以及释放这个锁,提高吞吐量)
缺:优先级翻转,饥饿的线程(某个线程一直得不到锁)持有锁的时间相对较长或者请求锁的平均时间间隔较长,应该使用公平锁(插队可能不会提高吞吐量)
【可重入锁】递归锁 ReentrantLock / Synchronized
指的是同一线程外层函数获得锁之后,在进入内层方法会自动获取锁
(线程可以进入任何一个它已经拥有的锁所同步的代码块)作用避免死锁
验证ReentrantLock 和Synchronized 的代码
加两把锁,只解一把:程序直接卡死,线程不能出来
一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,不断的判断是否能获取锁,直到获取到锁才会退出循环。
优:获取不到锁不会阻塞,一直都是active,一直处于用户态减少了不必要的上下文切换,执行速度快。
缺:while循环不断消耗CPU资源,busy waiting
原来提到的比较并交换,底层使用的就是自旋,自旋就是多次尝试,多次访问,不会阻塞的状态就是自旋。
【为什么Synchronized无法禁止指令重排,却能保证有序性】
为了提升计算机能力,在硬件层面做了一些优化,如处理器优化和指令重排,但这些技术的引用带来了有序性问题。即使重排,也必须遵守as-if-serial语义,也就是在单线程中不能影响程序的执行结果。把单线程保护起来,给程序员一种幻觉:单线程是按代码的顺序来执行的。
排他的、可重入的锁。当某个线程执行到一段被synchronized修饰的代码之前,会先进行加锁,执行完之后再进行解锁。在加锁之后,解锁之前,其他线程是无法再次获得锁的。保证了同一时间内,被synchronized修饰的代码是单线程执行的。
控制多个线程对共享资源的互斥访问,synchronized,ReentrantLock。
方法上 对象实例(this)||静态方法 对象对应的Class实例 || 对象实例 对应的代码块
【ReentrantLock】
除了能完成 synchronized 所能完成的所有工作外,还提供了诸如可响应中断锁、可轮询锁请求、定时锁等避免多线程死锁的方法。还可以与 Condition 类绑定,唤醒或等待指定条件线程。
join()在当前线程中调用另一个线程的 join() 方法,则当前线程会被挂起,转为阻塞状态而不是忙等待,直到另一个线程结束,当前线程再由阻塞状态变为就绪状态,等待 cpu 分配时间片。
适用场景:主线程生成并启动了子线程,需要用到子线程返回的结果,主线程需要在子线程结束后再结束
wait()、notify() await()、signal()
synchronized属于JVM层面,ReentrantLock 是 JDK (juc)实现的。
monitorenter 只能在同步块 或 方法中 才能调用 wait/ notify
synchronized:不需要用户去手动释放锁,不用担心死锁
synchronized不可中断 ReentrantLock:可设置超时方法, 也可lockInterrupible()
非公平
ReentrantLock:精确唤醒