张家港市住房城乡建设局网站,国外可以做非法网站吗,没有备案的交易网站,深圳市建局官网入职体验#xff1a;
今天运维岗位刚入职#xff0c;但是目前还没有办理入职手续#xff0c;但是领导发了一堆资料#xff01;看了一下#xff0c;非常多的新东西#xff0c;只能说努力一把#xff01;#xff01;#xff01; 一、锁的分类
1.1 可重入锁、不可重入锁…入职体验
今天运维岗位刚入职但是目前还没有办理入职手续但是领导发了一堆资料看了一下非常多的新东西只能说努力一把 一、锁的分类
1.1 可重入锁、不可重入锁
Java中提供的synchronizedReentrantLockReentrantReadWriteLock都是可重入锁。
重入当前线程获取到A锁在获取之后尝试再次获取A锁是可以直接拿到的。
不可重入当前线程获取到A锁在获取之后尝试再次获取A锁无法获取到的因为A锁被当前线程占用着需要等待自己释放锁再获取锁。
1.2 乐观锁、悲观锁
Java中提供的synchronizedReentrantLockReentrantReadWriteLock都是悲观锁。
Java中提供的CAS操作就是乐观锁的一种实现。
悲观锁获取不到锁资源时会将当前线程挂起进入BLOCKED、WAITING线程挂起会涉及到用户态和内核的太的切换而这种切换是比较消耗资源的。 用户态JVM可以自行执行的指令不需要借助操作系统执行。 内核态JVM不可以自行执行需要操作系统才可以执行。
乐观锁获取不到锁资源可以再次让CPU调度重新尝试获取锁资源。
Atomic原子性类中就是基于CAS乐观锁实现的。
1.3 公平锁、非公平锁
Java中提供的synchronized只能是非公平锁。
Java中提供的ReentrantLockReentrantReadWriteLock可以实现公平锁和非公平锁
公平锁线程A获取到了锁资源线程B没有拿到线程B去排队线程C来了锁被A持有同时线程B在排队。直接排到B的后面等待B拿到锁资源或者是B取消后才可以尝试去竞争锁资源。
非公平锁线程A获取到了锁资源线程B没有拿到线程B去排队线程C来了先尝试竞争一波 拿到锁资源开心插队成功。 没有拿到锁资源依然要排到B的后面等待B拿到锁资源或者是B取消后才可以尝试去竞争锁资源。
1.4 互斥锁、共享锁
Java中提供的synchronized、ReentrantLock是互斥锁。
Java中提供的ReentrantReadWriteLock有互斥锁也有共享锁。
互斥锁同一时间点只会有一个线程持有者当前互斥锁。
共享锁同一时间点当前共享锁可以被多个线程同时持有。
二、深入synchronized
2.1 类锁、对象锁
synchronized的使用一般就是同步方法和同步代码块。
synchronized的锁是基于对象实现的。
如果使用同步方法 static此时使用的是当前类.class作为锁类锁 非static此时使用的是当前对象做为锁对象锁 public class MiTest {
public static void main(String[] args) {// 锁的是当前Test.classTest.a();
Test test new Test();// 锁的是new出来的test对象test.b();}
}
class Test{public static synchronized void a(){System.out.println(1111);}
public synchronized void b(){System.out.println(2222);}
}
2.2 synchronized的优化
在JDK1.5的时候Doug Lee推出了ReentrantLocklock的性能远高于synchronized所以JDK团队就在JDK1.6中对synchronized做了大量的优化。
锁消除在synchronized修饰的代码中如果不存在操作临界资源的情况会触发锁消除你即便写了synchronized他也不会触发。
public synchronized void method(){// 没有操作临界资源// 此时这个方法的synchronized你可以认为木有~~
}
锁膨胀如果在一个循环中频繁的获取和释放做资源这样带来的消耗很大锁膨胀就是将锁的范围扩大避免频繁的竞争和获取锁资源带来不必要的消耗。
public void method(){for(int i 0;i 999999;i){synchronized(对象){
}}// 这是上面的代码会触发锁膨胀synchronized(对象){for(int i 0;i 999999;i){
}}
}
锁升级ReentrantLock的实现是先基于乐观锁的CAS尝试获取锁资源如果拿不到锁资源才会挂起线程。synchronized在JDK1.6之前完全就是获取不到锁立即挂起当前线程所以synchronized性能比较差。
synchronized就在JDK1.6做了锁升级的优化 无锁、匿名偏向当前对象没有作为锁存在。 偏向锁如果当前锁资源只有一个线程在频繁的获取和释放那么这个线程过来只需要判断当前指向的线程是否是当前线程 。 如果是直接拿着锁资源走。 如果当前线程不是我基于CAS的方式尝试将偏向锁指向当前线程。如果获取不到触发锁升级升级为轻量级锁。偏向锁状态出现了锁竞争的情况 轻量级锁会采用自旋锁的方式去频繁的以CAS的形式获取锁资源采用的是自适应自旋锁 如果成功获取到拿着锁资源走 如果自旋了一定次数没拿到锁资源锁升级。 重量级锁就是最传统的synchronized方式拿不到锁资源就挂起当前线程。用户态内核态
2.3 synchronized实现原理
synchronized是基于对象实现的。
先要对Java中对象在堆内存的存储有一个了解。 展开MarkWord MarkWord中标记着四种锁的信息无锁、偏向锁、轻量级锁、
2.4 synchronized的锁升级
为了可以在Java中看到对象头的MarkWord信息需要导入依赖
dependencygroupIdorg.openjdk.jol/groupIdartifactIdjol-core/artifactIdversion0.9/version
/dependency
锁默认情况下开启了偏向锁延迟。 偏向锁在升级为轻量级锁时会涉及到偏向锁撤销需要等到一个安全点STW才可以做偏向锁撤销在明知道有并发情况就可以选择不开启偏向锁或者是设置偏向锁延迟开启 因为JVM在启动时需要加载大量的.class文件到内存中这个操作会涉及到synchronized的使用为了避免出现偏向锁撤销操作JVM启动初期有一个延迟4s开启偏向锁的操作 如果正常开启偏向锁了那么不会出现无锁状态对象会直接变为匿名偏向 public static void main(String[] args) throws InterruptedException {Thread.sleep(5000);Object o new Object();System.out.println(ClassLayout.parseInstance(o).toPrintable());
new Thread(() - {
synchronized (o){//t1 - 偏向锁System.out.println(t1: ClassLayout.parseInstance(o).toPrintable());}}).start();//main - 偏向锁 - 轻量级锁CAS - 重量级锁synchronized (o){System.out.println(main: ClassLayout.parseInstance(o).toPrintable());}
}
整个锁升级状态的转变 Lock Record以及ObjectMonitor存储的内容 2.5 重量锁底层ObjectMonitor
需要去找到openjdk在百度中直接搜索openjdk第一个链接就是
找到ObjectMonitor的两个文件hppcpp
先查看核心属性jdk8u/jdk8u/hotspot: 69087d08d473 src/share/vm/runtime/objectMonitor.hpp
ObjectMonitor() {_header NULL; // header存储着MarkWord_count 0; // 竞争锁的线程个数_waiters 0, // wait的线程个数_recursions 0; // 标识当前synchronized锁重入的次数_object NULL;_owner NULL; // 持有锁的线程_WaitSet NULL; // 保存wait的线程信息双向链表_WaitSetLock 0 ;_Responsible NULL ;_succ NULL ;_cxq NULL ; // 获取锁资源失败后线程要放到当前的单向链表中FreeNext NULL ;_EntryList NULL ; // _cxq以及被唤醒的WaitSet中的线程在一定机制下会放到EntryList中_SpinFreq 0 ;_SpinClock 0 ;OwnerIsThread 0 ;_previous_owner_tid 0;}
适当的查看几个C中实现的加锁流程
jdk8u/jdk8u/hotspot: 69087d08d473 src/share/vm/runtime/objectMonitor.cpp
TryLock
int ObjectMonitor::TryLock (Thread * Self) {for (;;) {// 拿到持有锁的线程void * own _owner ;// 如果有线程持有锁告辞if (own ! NULL) return 0 ;// 说明没有线程持有锁own是nullcmpxchg指令就是底层的CAS实现。if (Atomic::cmpxchg_ptr (Self, _owner, NULL) NULL) {// 成功获取锁资源return 1 ;}// 这里其实重试操作没什么意义直接返回-1if (true) return -1 ;}
}
try_entry
bool ObjectMonitor::try_enter(Thread* THREAD) {// 在判断_owner是不是当前线程if (THREAD ! _owner) {// 判断当前持有锁的线程是否是当前线程说明轻量级锁刚刚升级过来的情况if (THREAD-is_lock_owned ((address)_owner)) {_owner THREAD ;_recursions 1 ;OwnerIsThread 1 ;return true;}// CAS操作尝试获取锁资源if (Atomic::cmpxchg_ptr (THREAD, _owner, NULL) ! NULL) {// 没拿到锁资源告辞return false;}// 拿到锁资源return true;} else {// 将_recursions 1代表锁重入操作。_recursions;return true;}
}
enter想方设法拿到锁资源如果没拿到挂起扔到_cxq单向链表中
void ATTR ObjectMonitor::enter(TRAPS) {// 拿到当前线程Thread * const Self THREAD ;void * cur ;// CAS走你cur Atomic::cmpxchg_ptr (Self, _owner, NULL) ;if (cur NULL) {// 拿锁成功return ;}// 锁重入操作if (cur Self) {// TODO-FIXME: check for integer overflow! BUGID 6557169._recursions ;return ;}//轻量级锁过来的。if (Self-is_lock_owned ((address)cur)) {_recursions 1 ;_owner Self ;OwnerIsThread 1 ;return ;}
// 走到这了没拿到锁资源countAtomic::inc_ptr(_count);
for (;;) {jt-set_suspend_equivalent();// 入队操作进到cxq中EnterI (THREAD) ;if (!ExitSuspendEquivalent(jt)) break ;_recursions 0 ;_succ NULL ;exit (false, Self) ;jt-java_suspend_self();}}// count--Atomic::dec_ptr(_count);}
EnterI
for (;;) {// 入队node._next nxt _cxq ;// CAS的方式入队。if (Atomic::cmpxchg_ptr (node, _cxq, nxt) nxt) break ;
// 重新尝试获取锁资源if (TryLock (Self) 0) {assert (_succ ! Self , invariant) ;assert (_owner Self , invariant) ;assert (_Responsible ! Self , invariant) ;return ;}
}