锁的种类
锁的种类Java中锁的种类大致分为偏向锁,自旋锁,轻量级锁,重量级锁。 ?偏向锁是一种编译解释锁。如果代码中不可能出现多线程并发争抢同一个锁的时候,JVM编译代码,解释执行的时候,会自动的放弃同步信息。消除synchronized的同步代码结果。使用锁标记的形式记录锁状态。在Monitor中有变量ACC_SYNCHRONIZED。当变量值使用的时候,代表偏向锁锁定。可以避免锁的争抢和锁池状态的维护。提高效率。 自旋锁是一个过渡锁,是偏向锁和轻量级锁的过渡。 轻量级锁过渡锁。当偏向锁不满足,也就是有多线程并发访问,锁定同一个对象的时候,先提升为轻量级锁。也是使用标记ACC_SYNCHRONIZED标记记录的。ACC_UNSYNCHRONIZED标记记录未获取到锁信息的线程。就是只有两个线程争抢锁标记的时候,优先使用轻量级锁。 重量级锁下面说的实现就是重量级锁 锁的底层实现Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现。同步方法 并不是由 monitor enter 和 monitor exit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的。 当在对象上加锁时,数据是记录在对象头中。当执行synchronized同步方法或同步代码块时,会在对象头中记录锁标记,锁标记指向的是monitor对象(也称为管程或监视器锁)的起始地址。每个对象都存在着一个 monitor 与之关联,对象与其 monitor 之间的关系有存在多种实现方式,如monitor可以与对象一起创建销毁或当线程试图获取对象锁时自动生成,但当一个 monitor 被某个线程持有后,它便处于锁定状态。 过程:当多线程并发访问同一个同步代码时,首先会进入_EntryList,当线程获取锁标记后,monitor中的_Owner记录此线程,并在monitor中的计数器执行递增计算(+1),代表锁定,其他线程在_EntryList中继续阻塞。若执行线程调用wait方法,则monitor中的计数器执行赋值为0计算,并将_Owner标记赋值为null,代表放弃锁,执行线程进如_WaitSet中阻塞。若执行线程调用notify/notifyAll方法,_WaitSet中的线程被唤醒,进入_EntryList中阻塞,等待获取锁标记。若执行线程的同步代码执行结束,同样会释放锁标记,monitor中的_Owner标记赋值为null,且计数器赋值为0计算。 ?PS对象头:存储对象的hashCode、锁信息或分代年龄或GC标志,类型指针指向对象的类元数据,JVM通过这个指针确定该对象是哪个类的实例等信息。 ReentrantLock重入锁,建议应用的同步方式。相对效率比synchronized高。量级较轻。(synchronized在JDK1.5版本开始,尝试优化。到JDK1.7版本后,优化效率已经非常好了。在绝对效率上,不比reentrantLock差多少) 公平锁private static ReentrantLock lock = new ReentrantLock(true); 尝试锁void m2(){ boolean isLocked = false; try{ // 尝试锁, 如果有锁,无法获取锁标记,返回false。 // 如果获取锁标记,返回true // isLocked = lock.tryLock(); // 阻塞尝试锁,阻塞参数代表的时长,尝试获取锁标记。 // 如果超时,不等待。直接返回。 isLocked = lock.tryLock(5,TimeUnit.SECONDS); if(isLocked){ System.out.println("m2() method synchronized"); }else{ System.out.println("m2() method unsynchronized"); } }catch(Exception e){ e.printStackTrace(); }finally{ if(isLocked){ // 尝试锁在解除锁标记的时候,一定要判断是否获取到锁标记。 // 如果当前线程没有获取到锁标记,会抛出异常。 lock.unlock(); } } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |