Java多线程之锁的状态有哪些(java,开发技术)

时间:2024-05-05 13:06:15 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

    阻塞锁

    含义:多个线程同时调用一个方法的时候,所有的线程都被排队处理了,让线程进入阻塞状态进行等待,当获得相应的信号(唤醒、时间)时,才能进入线程的准备就绪的状态。通过竞争。进入运行状态。

    Java中,能够进入\退出、阻塞状态或包含阻塞锁的方法有 ,synchronized 关键字(其中的重量锁),ReentrantLock,Object.wait()\notify()

    实例代码如下

    //在main方法中,开启100个线程执行lock()方法//开启10个线程执行unlock()方法//此时进行加锁和解锁之后,加锁多于解锁的时候,就会一直阻塞等待publicclassDemo01{privatebooleanisLocked=false;publicsynchronizedvoidlock()throwsInterruptedException{while(isLocked){//当其他线程进来,即处于等待阻塞状态wait();}System.out.println("Demo01.lock");isLocked=true;}publicsynchronizedvoidunlock(){isLocked=false;System.out.println("Demo01.unlock");notify();}publicstaticvoidmain(String[]args){Demo01demo01=newDemo01();for(inti=0;i<100;i++){newThread(()->{try{demo01.lock();}catch(InterruptedExceptione){e.printStackTrace();}}).start();}for(inti=0;i<10;i++){newThread(()->{demo01.unlock();}).start();}}}

    由于被调用的方法越耗时,线程越多的时候,等待的线程等待的时间也就越长,甚至于几分钟或者几十分钟。对于Web等对反应时间要求很高的系统来说,这是不可行的,因此需要让其非阻塞,可以在没有拿到锁之后马上返回,告诉客户稍后重试。

    非阻塞锁

    含义:多个线程同时调用一个方法的时候,当某一个线程最先获取到锁,这时其他线程没拿到锁,这时就直接返回,只有当最先获取的锁的线程释放锁,其他线程的锁才能进来,在它释放之前其他线程都会获取失败

    代码实现如下

    //非阻塞锁publicclassDemo02{privatebooleanisLocked=false;publicsynchronizedbooleanlock()throwsInterruptedException{if(isLocked){returnfalse;}System.out.println("Demo01.lock");isLocked=true;returntrue;}publicsynchronizedbooleanunlock(){if(isLocked){System.out.println("Demo01.unlock");isLocked=!isLocked;returntrue;}returnfalse;}publicstaticvoidmain(String[]args){Demo02demo01=newDemo02();for(inti=0;i<100;i++){newThread(()->{try{if(demo01.lock()){System.out.println("获取锁失败");}}catch(InterruptedExceptione){e.printStackTrace();}}).start();}for(inti=0;i<1000;i++){newThread(()->{if(demo01.unlock()){System.out.println("解锁成功");}}).start();}}}

    锁的四种状态

    Java多线程之锁的状态有哪些

    Java多线程之锁的状态有哪些

    锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁(但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级)JDK 1.6中默认是开启偏向锁和轻量级锁的,

    注意:HotSpot JVM是支持锁降级的 但是因为降级的效率太低了,所以在开发中不使用降级的操作

    但是锁的状态时存在哪里的呢?

    锁存在Java的对象头中的Mark Work。Mark Work默认不仅存放着锁标志位,还存放对象hashCode等信息。运行时,会根据锁的状态,修改Mark Work的存储内容。如果对象是数组类型,则虚拟机用3个字宽存储对象头,如果对象是非数组类型,则用2字宽存储对象头。在32位虚拟机中,一字宽等于四字节,即32bit。

    字宽(Word): 内存大小的单位概念, 对于 32 位处理器 1 Word = 4 Bytes, 64 位处理器 1 Word = 8 Bytes

    每一个 Java 对象都至少占用 2 个字宽的内存(数组类型占用3个字宽)。

    • 第一个字宽也被称为对象头Mark Word。 对象头包含了多种不同的信息, 其中就包含对象锁相关的信息。

    • 第二个字宽是指向定义该对象类信息(class metadata)的指针

    Java多线程之锁的状态有哪些

    无锁状态

    在代码刚刚进入同步块的时候,就处于无锁状态。

    偏向锁

    概念:偏向锁会偏向于第一个访问锁的线程,如果在接下来的运行过程中,该锁没有被其他的线程访问,则持有偏向锁的线程将永远不需要触发同步。也就是说,偏向锁在资源无竞争的情况下消除了同步语句,连CAS操作都不做了,提高了程序的运行性能

    引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量锁执行路径。因为轻量级锁的获取以及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次原子指令(由于一旦出现多线程竞争的情况就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须小于节省下来的CAS原子指令的性能消耗)上面说过,轻量级锁是为了在多线程交替执行同步块时提高性能,而偏向锁则是在只有一个线程执行同步块的时候进一步提高性能。

    轻量级锁

    “轻量级”是相对于使用操作系统互斥量来实现传统锁而言的。但是首先需要强调一点的是,轻量级锁并不是用来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用产生的性能消耗。

    在解释轻量级锁的执行过程过程之前,我们要先明白一点,轻量级锁使用的场景是线程交替同步块的情况,如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级。

    重量级锁

    synchronized是通过对象内部的一个监视器锁(monitor)实现的。但是monitor底层又依赖于底层操作系统的Mutex Lock实现的。而操作系统实现线程之间的切换就需要从用户态切换到核心态,切换的成本很高,状态之间的转化需要相对比较长的时间,这就是synchronized效率低的原因,因此,这种依赖于操作系统的Mutex Lock所实现的锁被称之为“重量级锁”

    可重入锁

    可重入锁也叫做递归锁,指的是同一线程外层函数获得锁之后,内存递归函数仍然有获得该锁的代码,但是不受影响

    Java中ReentrantLock和synchronized都是可重入锁 自旋锁不是可重入锁

    可重入锁的最大作用就是避免死锁

    下面是一段实例代码

    publicclassTestimplementsRunnable{publicsynchronizedvoidget(){System.out.println(Thread.currentThread().getId());set();}publicsynchronizedvoidset(){System.out.println(Thread.currentThread().getId());}@Overridepublicvoidrun(){get();}publicstaticvoidmain(String[]args){Testss=newTest();newThread(ss).start();newThread(ss).start();newThread(ss).start();}}//执行结果//11//11//12//12//13//13
    publicclassTest2implementsRunnable{ReentrantLocklock=newReentrantLock();publicvoidget(){lock.lock();try{System.out.println(Thread.currentThread().getId());set();}catch(Exceptione){e.printStackTrace();}finally{lock.unlock();}}publicvoidset(){lock.lock();try{System.out.println(Thread.currentThread().getId());}catch(Exceptione){e.printStackTrace();}finally{lock.unlock();}}@Overridepublicvoidrun(){get();}publicstaticvoidmain(String[]args){Testss=newTest();newThread(ss).start();newThread(ss).start();newThread(ss).start();}}//执行结果//11//11//12//12//13//13

    自旋锁

    publicclassSpinLock{privateAtomicReference<Thread>owner=newAtomicReference<>();publicvoidlock(){Threadcurrent=Thread.currentThread();while(!owner.compareAndSet(null,current)){}}publicvoidunlock(){Threadcurrent=Thread.currentThread();owner.compareAndSet(current,null);}

    改进的自旋锁

    publicclassSpinLock1{privateAtomicReference<Thread>owner=newAtomicReference<>();privateintcount=0;publicvoidlock(){Threadcurrent=Thread.currentThread();if(current==owner.get()){count++;return;}while(!owner.compareAndSet(null,current)){}}publicvoidunlock(){Threadcurrent=Thread.currentThread();if(current==owner.get()){if(count!=0){count--;}else{owner.compareAndSet(current,null);}}}}

    读写锁

    Lock接口以及其对象,使用它,很优雅的控制了竞争资源的安全访问,但是这种锁不区别读写 - 为普通锁

    为了提高性能,Java提供了读写锁,在读的地方使用读锁,在写的时候使用写锁,灵活控制,如果没有写锁的情况下。读是无阻塞的,在一定情况下提高了程序的执行效率。下面我们来看源代码(Lock接口和Condition接口在上一篇Java - 多线程 - 锁和提升 第1篇已经分析过,此处不再分析)

    publicclassReentrantReadWriteLockimplementsReadWriteLock,java.io.Serializable{privatestaticfinallongserialVersionUID=-6992448646407690164L;//读锁内部类对象privatefinalReentrantReadWriteLock.ReadLockreaderLock;//写锁内部类对象privatefinalReentrantReadWriteLock.WriteLockwriterLock;//执行所有的同步机制finalSyncsync;//无参构造函数调用有参构造ReentrantReadWriteLock(booleanfair)publicReentrantReadWriteLock(){this(false);}//有参构造函数是否创建公平锁publicReentrantReadWriteLock(booleanfair){sync=fair?newFairSync():newNonfairSync();readerLock=newReadLock(this);writerLock=newWriteLock(this);}//返回读锁和写锁的对象publicReentrantReadWriteLock.WriteLockwriteLock(){returnwriterLock;}publicReentrantReadWriteLock.ReadLockreadLock(){returnreaderLock;}//继承自AbstractQueueSynchronizer的SyncabstractstaticclassSyncextendsAbstractQueuedSynchronizer{privatestaticfinallongserialVersionUID=6317671515068378041L;//共享移位staticfinalintSHARED_SHIFT=16;//共享单位staticfinalintSHARED_UNIT=(1<<SHARED_SHIFT);//最大数量staticfinalintMAX_COUNT=(1<<SHARED_SHIFT)-1;//独占staticfinalintEXCLUSIVE_MASK=(1<<SHARED_SHIFT)-1;//返回共享锁的数量staticintsharedCount(intc){returnc>>>SHARED_SHIFT;}//返回独占锁的数量staticintexclusiveCount(intc){returnc&EXCLUSIVE_MASK;}//每线程读取保持计数的计数器staticfinalclassHoldCounter{intcount=0;//使用id而不是使用引用避免垃圾残留finallongtid=getThreadId(Thread.currentThread());}//线程本地子类staticfinalclassThreadLocalHoldCounterextendsThreadLocal<HoldCounter>{publicHoldCounterinitialValue(){returnnewHoldCounter();}}//当前线程持有的可重入锁的数量,只在构造函数和readObject中初始化,//当线程的读取保持计数下降到0的时候删除privatetransientThreadLocalHoldCounterreadHolds;//获取readLock的最后一个线程的保持计数privatetransientHoldCountercachedHoldCounter;//第一个获得读的线程privatetransientThreadfirstReader=null;//计数器privatetransientintfirstReaderHoldCount;Sync(){readHolds=newThreadLocalHoldCounter();setState(getState());//ensuresvisibilityofreadHolds}//当前线程获取锁的时候由于策略超过其他等待线程而应阻止,返回trueabstractbooleanreaderShouldBlock();//如果由于试图超越其他等待线程的策略而导致当前线程在尝试获取写锁(且有资格这样做)//时应阻塞,则返回trueabstractbooleanwriterShouldBlock();//尝试释放protectedfinalbooleantryRelease(intreleases){if(!isHeldExclusively())thrownewIllegalMonitorStateException();intnextc=getState()-releases;booleanfree=exclusiveCount(nextc)==0;if(free)setExclusiveOwnerThread(null);setState(nextc);returnfree;}//尝试增加protectedfinalbooleantryAcquire(intacquires){/**Walkthrough:*1.Ifreadcountnonzeroorwritecountnonzero*andownerisadifferentthread,fail.*2.Ifcountwouldsaturate,fail.(Thiscanonly*happenifcountisalreadynonzero.)*3.Otherwise,thisthreadiseligibleforlockif*itiseitherareentrantacquireor*queuepolicyallowsit.Ifso,updatestate*andsetowner.*/Threadcurrent=Thread.currentThread();intc=getState();intw=exclusiveCount(c);if(c!=0){//(Note:ifc!=0andw==0thensharedcount!=0)if(w==0||current!=getExclusiveOwnerThread())returnfalse;if(w+exclusiveCount(acquires)>MAX_COUNT)thrownewError("Maximumlockcountexceeded");//ReentrantacquiresetState(c+acquires);returntrue;}if(writerShouldBlock()||!compareAndSetState(c,c+acquires))returnfalse;setExclusiveOwnerThread(current);returntrue;}//尝试释放共享锁protectedfinalbooleantryReleaseShared(intunused){Threadcurrent=Thread.currentThread();if(firstReader==current){//assertfirstReaderHoldCount>0;if(firstReaderHoldCount==1)firstReader=null;elsefirstReaderHoldCount--;}else{HoldCounterrh=cachedHoldCounter;if(rh==null||rh.tid!=getThreadId(current))rh=readHolds.get();intcount=rh.count;if(count<=1){readHolds.remove();if(count<=0)throwunmatchedUnlockException();}--rh.count;}for(;;){intc=getState();intnextc=c-SHARED_UNIT;if(compareAndSetState(c,nextc))//Releasingthereadlockhasnoeffectonreaders,//butitmayallowwaitingwriterstoproceedif//bothreadandwritelocksarenowfree.returnnextc==0;}}//非法的监视器状态异常privateIllegalMonitorStateExceptionunmatchedUnlockException(){returnnewIllegalMonitorStateException("attempttounlockreadlock,notlockedbycurrentthread");}//尝试加共享锁protectedfinalinttryAcquireShared(intunused){/**Walkthrough:*1.Ifwritelockheldbyanotherthread,fail.*2.Otherwise,thisthreadiseligiblefor*lockwrtstate,soaskifitshouldblock*becauseofqueuepolicy.Ifnot,try*tograntbyCASingstateandupdatingcount.*Notethatstepdoesnotcheckforreentrant*acquires,whichispostponedtofullversion*toavoidhavingtocheckholdcountin*themoretypicalnon-reentrantcase.*3.Ifstep2failseitherbecausethread*apparentlynoteligibleorCASfailsorcount*saturated,chaintoversionwithfullretryloop.*/Threadcurrent=Thread.currentThread();intc=getState();if(exclusiveCount(c)!=0&&getExclusiveOwnerThread()!=current)return-1;intr=sharedCount(c);if(!readerShouldBlock()&&r<MAX_COUNT&&compareAndSetState(c,c+SHARED_UNIT)){if(r==0){firstReader=current;firstReaderHoldCount=1;}elseif(firstReader==current){firstReaderHoldCount++;}else{HoldCounterrh=cachedHoldCounter;if(rh==null||rh.tid!=getThreadId(current))cachedHoldCounter=rh=readHolds.get();elseif(rh.count==0)readHolds.set(rh);rh.count++;}return1;}returnfullTryAcquireShared(current);}//读取的完整版本,可处理CAS丢失和tryAcquireShared中未处理的可重入读取finalintfullTryAcquireShared(Threadcurrent){HoldCounterrh=null;for(;;){intc=getState();if(exclusiveCount(c)!=0){if(getExclusiveOwnerThread()!=current)return-1;}elseif(readerShouldBlock()){if(firstReader==current){//assertfirstReaderHoldCount>0;}else{if(rh==null){rh=cachedHoldCounter;if(rh==null||rh.tid!=getThreadId(current)){rh=readHolds.get();if(rh.count==0)readHolds.remove();}}if(rh.count==0)return-1;}}if(sharedCount(c)==MAX_COUNT)thrownewError("Maximumlockcountexceeded");if(compareAndSetState(c,c+SHARED_UNIT)){if(sharedCount(c)==0){firstReader=current;firstReaderHoldCount=1;}elseif(firstReader==current){firstReaderHoldCount++;}else{if(rh==null)rh=cachedHoldCounter;if(rh==null||rh.tid!=getThreadId(current))rh=readHolds.get();elseif(rh.count==0)readHolds.set(rh);rh.count++;cachedHoldCounter=rh;//cacheforrelease}return1;}}}//执行tryLock进行写入,从而在两种模式下都可以进行插入,这与tryAcquire的作用相同//只是缺少对writerShouldBlock的调用finalbooleantryWriteLock(){Threadcurrent=Thread.currentThread();intc=getState();if(c!=0){intw=exclusiveCount(c);if(w==0||current!=getExclusiveOwnerThread())returnfalse;if(w==MAX_COUNT)thrownewError("Maximumlockcountexceeded");}if(!compareAndSetState(c,c+1))returnfalse;setExclusiveOwnerThread(current);returntrue;}//执行tryLock进行读取,从而在两种模式下都可以进行插入。//除了没有调用readerReaderShouldBlock以外,这与tryAcquireShared的作用相同。finalbooleantryReadLock(){Threadcurrent=Thread.currentThread();for(;;){intc=getState();if(exclusiveCount(c)!=0&&getExclusiveOwnerThread()!=current)returnfalse;intr=sharedCount(c);if(r==MAX_COUNT)thrownewError("Maximumlockcountexceeded");if(compareAndSetState(c,c+SHARED_UNIT)){if(r==0){firstReader=current;firstReaderHoldCount=1;}elseif(firstReader==current){firstReaderHoldCount++;}else{HoldCounterrh=cachedHoldCounter;if(rh==null||rh.tid!=getThreadId(current))cachedHoldCounter=rh=readHolds.get();elseif(rh.count==0)readHolds.set(rh);rh.count++;}returntrue;}}}//是否独占锁protectedfinalbooleanisHeldExclusively(){returngetExclusiveOwnerThread()==Thread.currentThread();}//返回Condition对象finalConditionObjectnewCondition(){returnnewConditionObject();}//返回独占锁finalThreadgetOwner(){return((exclusiveCount(getState())==0)?null:getExclusiveOwnerThread());}//获取readLock所得个数finalintgetReadLockCount(){returnsharedCount(getState());}//判断是否是writeLock锁finalbooleanisWriteLocked(){returnexclusiveCount(getState())!=0;}//获得写锁的数量finalintgetWriteHoldCount(){returnisHeldExclusively()?exclusiveCount(getState()):0;}//获得读锁的数量finalintgetReadHoldCount(){if(getReadLockCount()==0)return0;Threadcurrent=Thread.currentThread();if(firstReader==current)returnfirstReaderHoldCount;HoldCounterrh=cachedHoldCounter;if(rh!=null&&rh.tid==getThreadId(current))returnrh.count;intcount=readHolds.get().count;if(count==0)readHolds.remove();returncount;}//序列化privatevoidreadObject(java.io.ObjectInputStreams)throwsjava.io.IOException,ClassNotFoundException{s.defaultReadObject();readHolds=newThreadLocalHoldCounter();setState(0);//resettounlockedstate}//获取数量finalintgetCount(){returngetState();}}//非公平锁staticfinalclassNonfairSyncextendsSync{privatestaticfinallongserialVersionUID=-8159625535654395037L;finalbooleanwriterShouldBlock(){returnfalse;//writerscanalwaysbarge}finalbooleanreaderShouldBlock(){returnapparentlyFirstQueuedIsExclusive();}}//公平锁staticfinalclassFairSyncextendsSync{privatestaticfinallongserialVersionUID=-2274990926593161451L;finalbooleanwriterShouldBlock(){returnhasQueuedPredecessors();}finalbooleanreaderShouldBlock(){returnhasQueuedPredecessors();}}//读锁实现了Lock接口publicstaticclassReadLockimplementsLock,java.io.Serializable{privatestaticfinallongserialVersionUID=-5992448646407690164L;privatefinalSyncsync;//初始化,获得是否是公平读锁protectedReadLock(ReentrantReadWriteLocklock){sync=lock.sync;}//加锁是共享锁publicvoidlock(){sync.acquireShared(1);}//锁中断publicvoidlockInterruptibly()throwsInterruptedException{sync.acquireSharedInterruptibly(1);}//调用加锁publicbooleantryLock(){returnsync.tryReadLock();}//超时加锁publicbooleantryLock(longtimeout,TimeUnitunit)throwsInterruptedException{returnsync.tryAcquireSharedNanos(1,unit.toNanos(timeout));}//解锁publicvoidunlock(){sync.releaseShared(1);}//Condition对象publicConditionnewCondition(){thrownewUnsupportedOperationException();}//toString方法publicStringtoString(){intr=sync.getReadLockCount();returnsuper.toString()+"[Readlocks="+r+"]";}}//写锁publicstaticclassWriteLockimplementsLock,java.io.Serializable{privatestaticfinallongserialVersionUID=-4992448646407690164L;privatefinalSyncsync;//写锁初始化protectedWriteLock(ReentrantReadWriteLocklock){sync=lock.sync;}//加锁publicvoidlock(){sync.acquire(1);}//锁中断publicvoidlockInterruptibly()throwsInterruptedException{sync.acquireInterruptibly(1);}//加锁publicbooleantryLock(){returnsync.tryWriteLock();}//超时加锁publicbooleantryLock(longtimeout,TimeUnitunit)throwsInterruptedException{returnsync.tryAcquireNanos(1,unit.toNanos(timeout));}//解锁,调用release减1publicvoidunlock(){sync.release(1);}//Condition对象publicConditionnewCondition(){returnsync.newCondition();}//toString方法publicStringtoString(){Threado=sync.getOwner();returnsuper.toString()+((o==null)?"[Unlocked]":"[Lockedbythread"+o.getName()+"]");}//是否有独占的线程publicbooleanisHeldByCurrentThread(){returnsync.isHeldExclusively();}//获取写锁的数量publicintgetHoldCount(){returnsync.getWriteHoldCount();}}//判断是否是公平锁publicfinalbooleanisFair(){returnsyncinstanceofFairSync;}//判断是否是独占锁protectedThreadgetOwner(){returnsync.getOwner();}//获取读锁的个数publicintgetReadLockCount(){returnsync.getReadLockCount();}//判断是否是写锁publicbooleanisWriteLocked(){returnsync.isWriteLocked();}//判断锁是否被当前线程持有publicbooleanisWriteLockedByCurrentThread(){returnsync.isHeldExclusively();}//查询当前线程对该锁的可重入写入次数publicintgetWriteHoldCount(){returnsync.getWriteHoldCount();}//查询当前线程对该锁的可重读次数publicintgetReadHoldCount(){returnsync.getReadHoldCount();}//返回写线程的集合protectedCollection<Thread>getQueuedWriterThreads(){returnsync.getExclusiveQueuedThreads();}//返回读线程的集合protectedCollection<Thread>getQueuedReaderThreads(){returnsync.getSharedQueuedThreads();}//查询是否有任何线程正在等待获取读或写锁publicfinalbooleanhasQueuedThreads(){returnsync.hasQueuedThreads();}//查询指定线程是否在等待读或者写锁publicfinalbooleanhasQueuedThread(Threadthread){returnsync.isQueued(thread);}//获取等待队列的长度publicfinalintgetQueueLength(){returnsync.getQueueLength();}//获得等待队列中的线程集合protectedCollection<Thread>getQueuedThreads(){returnsync.getQueuedThreads();}//查询是否有任何线程正在等待与写锁关联的给定条件publicbooleanhasWaiters(Conditioncondition){if(condition==null)thrownewNullPointerException();if(!(conditioninstanceofAbstractQueuedSynchronizer.ConditionObject))thrownewIllegalArgumentException("notowner");returnsync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);}//查询是否有任何线程正在等待与写锁关联的给定条件的长度publicintgetWaitQueueLength(Conditioncondition){if(condition==null)thrownewNullPointerException();if(!(conditioninstanceofAbstractQueuedSynchronizer.ConditionObject))thrownewIllegalArgumentException("notowner");returnsync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);}//获得给定条件等待线程的集合protectedCollection<Thread>getWaitingThreads(Conditioncondition){if(condition==null)thrownewNullPointerException();if(!(conditioninstanceofAbstractQueuedSynchronizer.ConditionObject))thrownewIllegalArgumentException("notowner");returnsync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);}//toString()方法publicStringtoString(){intc=sync.getCount();intw=Sync.exclusiveCount(c);intr=Sync.sharedCount(c);returnsuper.toString()+"[Writelocks="+w+",Readlocks="+r+"]";}//获取线程IDstaticfinallonggetThreadId(Threadthread){returnUNSAFE.getLongVolatile(thread,TID_OFFSET);}//Unsafe对象privatestaticfinalsun.misc.UnsafeUNSAFE;privatestaticfinallongTID_OFFSET;static{try{UNSAFE=sun.misc.Unsafe.getUnsafe();Class<?>tk=Thread.class;TID_OFFSET=UNSAFE.objectFieldOffset(tk.getDeclaredField("tid"));}catch(Exceptione){thrownewError(e);}}}

    总结:

    • 从读写锁的实现代码,我们可以看到,其本质还是使用了AQS的独占锁和共享锁实现了读写分离。

    • 当需要进行读的时候使用共享锁,当需要写的时候使用独占锁

    • 同时,ReentrantReadWriteLock也提供了默认的非公平机制,当然也可以使用构造方法设置是否是公平锁

    互斥锁

    互斥锁指的是一次最多只能有一个线程持有的锁。如Java的Lock

    互斥锁也是为了保护共享资源的同步,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。互斥锁和自旋锁在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁。

    悲观锁

    悲观锁,顾名思义就是狠悲观,认为每次拿去的数据都会被修改,所以在每次拿锁的时候都会上锁,这样别人想拿到这个数据就会block到直到他拿到锁。传统的数据库就使用了很多的这种的机制:如行锁、表锁、读锁、写锁等,都是在做操作之前上锁。共享锁、排他锁、独占锁是悲观锁的一种实现。

    Java中的悲观锁,最典型的就是synchronized。而AQS框架下的锁,先尝试使用CAS乐观锁去获取锁,获取不到才会转为悲观锁,如ReentrantLock

    乐观锁

    乐观锁,顾名思义就是很乐观,每次拿去的数据都认为不会被修改,所以不会上锁。但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,对于此过程出现的ABA问题,可以使用版本号进行控制。

    乐观锁用于多读的场景,数据库提供的类似于write_condition机制都是使用了乐观锁,使用CAS保证这个操作的原子性

    乐观锁和悲观锁的比较

    • 乐观锁不是数据库自己实现的,悲观锁是数据库自己实现的

    • 两种锁各有优缺点,不能认为一种好于另外一种,乐观锁适用于写少的场景,也就是冲突发生很少的情况,这样省了锁的开销,加大了系统的吞吐量。

    • 但是如果经常发生冲突,上次应用不会retry,此时为了保证安全和维持性能,应该使用悲观锁

    公平锁

    公平锁。就是字面意思,公平的,是非抢占式的,一个一个排好队,等待执行,但是有缺点。如果某个线程执行的时间过长,会导致阻塞。比如ReentrantLock中的内部类 FairSync和ReentrantReadWriteLock中的内部类FairSync都是公平锁

    非公平锁

    非公平锁,及时字面以自,抢占式的,不管谁先来,谁后来,抢到了就是我的。比如ReentrantLock中的内部类 NonfairSync和ReentrantReadWriteLock中的内部类NonfairSync都是非公平锁

    显示锁和内置锁

    显示锁,是人为手动的锁,如:ReentrantLock、Lock锁,也就是说,实现了Lock的锁都是显示锁

    内置锁:内置锁使用synchronized,内置锁是互斥锁

    Java中每个对象都可以用作一个实现同步的锁。 线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。

    轮询锁和定时锁

    由tryLock实现,与无条件获取锁模式相比,它们具有更完善的错误恢复机制。可避免死锁的发生

    boolean tryLock():仅在调用时锁为空闲状态才获取该锁。如果锁可用,则获取锁,并立即返回值 true。如果锁不可用,则此方法将立即返回值 false。

    tryLock的重载 tryLock(time,TimeUnit)就是定时锁

    对象锁和类锁

    Java的对象锁和类锁,其实也就是 Java - 多线程 - 锁和提升 第1篇开篇所说的8锁 8锁核心思想

    • 关键字在实例方法上,锁为当前实例

    • 关键字在静态方法上,锁为当前Class对象

    • 关键字在代码块上,锁为括号里面的对象

    1.对象锁和类锁在基本概念上和内置锁是一致的,但是,两个锁是有很大的区别,对象锁适用于对象的实例方法,或者一个对象实例上的,类锁是作用于类的静态方法或者一个类的class对象上的。

    2.类的实例可以有多个,但是每个类只有一个class对象,不同实例的对象锁是互不相干的,但是每个类只有一个类锁。

    3.其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮我们理解锁定实例方法和静态方法的区别。

    4.synchronized只是一个内置的加锁机制,当某个方法加上synchronized关键字的后,就表明要获得该内置锁才能执行,并不能阻止其他线程访问不需要获得该锁的方法。

    5.调用对象的wait()方法的时候,会释放持有的对象锁,以便于调用 notify() 方法使用。notify()调用之后,会等到notify()所在的线程执行完毕之后再释放锁。

    锁粗化

    就是将多次连接在一起的加锁、解锁操作合并为一次,将多个连续的锁拓展为一个更大的锁。

    通常情况下,为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽可能短,但是大某些情况下,一个程序对同一个锁不间断、高频地请求、同步与释放,会消耗掉一定的系统资源,因为锁的讲求、同步与释放本身会带来性能损耗,这样高频的锁请求就反而不利于系统性能的优化了,虽然单次同步操作的时间可能很短。锁粗化就是告诉我们任何事情都有个度,有些情况下我们反而希望把很多次锁的请求合并成一个请求,以降低短时间内大量锁请求、同步、释放带来的性能损耗。

    锁消除

    锁消除即:删除不必要的加锁操作。根据代码逃逸技术,如果判断到一段代码中,堆上的数据不会逃逸出当前线程。那么可以认定这段代码是线程安全的,不必要加锁。

    锁消除是发生在编译器级别的一种锁优化方式。有时候我们写的代码完全不需要加锁,却执行了加锁操作。

    比如,StringBuffer类的append操作:

    @OverridepublicsynchronizedStringBufferappend(Stringstr){toStringCache=null;super.append(str);returnthis;}

    从源码中可以看出,append方法用了synchronized关键词,它是线程安全的。但我们可能仅在线程内部把StringBuffer当作局部变量使用:

    publicclassDemo{publicstaticvoidmain(String[]args){longstart=System.currentTimeMillis();intsize=10000;for(inti=0;i<size;i++){createStringBuffer("ic","java");}longtimeCost=System.currentTimeMillis()-start;System.out.println("createStringBuffer:"+timeCost+"ms");}publicstaticStringcreateStringBuffer(Stringstr1,Stringstr2){StringBuffersBuf=newStringBuffer();//append方法是同步操作sBuf.append(str1);sBuf.append(str2);returnsBuf.toString();}}

    代码中createStringBuffer方法中的局部对象sBuf,就只在该方法内的作用域有效,不同线程同时调用createStringBuffer()方法时,都会创建不同的sBuf对象,因此此时的append操作若是使用同步操作,就是白白浪费的系统资源。

    这时我们可以通过编译器将其优化,将锁消除,前提是java必须运行在server模式(server模式会比client模式作更多的优化),同时必须开启逃逸分析:

    -server -XX:+DoEscapeAnalysis -XX:+EliminateLocks

    其中+DoEscapeAnalysis表示开启逃逸分析,+EliminateLocks表示锁消除。

    逃逸分析:比如上面的代码,它要看sBuf是否可能逃出它的作用域?如果将sBuf作为方法的返回值进行返回,那么它在方法外部可能被当作一个全局对象使用,就有可能发生线程安全问题,这时就可以说sBuf这个对象发生逃逸了,因而不应将append操作的锁消除,但我们上面的代码没有发生锁逃逸,锁消除就可以带来一定的性能提升。

    信号量

    信号量有一个线程同步工具:Semaphore

    下面我们来分析一下源码

    publicclassSemaphoreimplementsjava.io.Serializable{privatestaticfinallongserialVersionUID=-3222578661600680210L;//Sync锁privatefinalSyncsync;//Sync实现了AbstractQueueSynchronizerabstractstaticclassSyncextendsAbstractQueuedSynchronizer{privatestaticfinallongserialVersionUID=1192457210091910933L;//初始化版本初始值Sync(intpermits){setState(permits);}//获取状态finalintgetPermits(){returngetState();}//不公平尝试共享finalintnonfairTryAcquireShared(intacquires){for(;;){intavailable=getState();intremaining=available-acquires;if(remaining<0||compareAndSetState(available,remaining))returnremaining;}}//尝试释放共享protectedfinalbooleantryReleaseShared(intreleases){for(;;){intcurrent=getState();intnext=current+releases;if(next<current)//overflowthrownewError("Maximumpermitcountexceeded");if(compareAndSetState(current,next))returntrue;}}//减少状态的指定值finalvoidreducePermits(intreductions){for(;;){intcurrent=getState();intnext=current-reductions;if(next>current)//underflowthrownewError("Permitcountunderflow");if(compareAndSetState(current,next))return;}}finalintdrainPermits(){for(;;){intcurrent=getState();if(current==0||compareAndSetState(current,0))returncurrent;}}}//非公平锁staticfinalclassNonfairSyncextendsSync{privatestaticfinallongserialVersionUID=-2694183684443567898L;NonfairSync(intpermits){super(permits);}protectedinttryAcquireShared(intacquires){returnnonfairTryAcquireShared(acquires);}}//公平锁staticfinalclassFairSyncextendsSync{privatestaticfinallongserialVersionUID=2014338818796000944L;FairSync(intpermits){super(permits);}protectedinttryAcquireShared(intacquires){for(;;){if(hasQueuedPredecessors())return-1;intavailable=getState();intremaining=available-acquires;if(remaining<0||compareAndSetState(available,remaining))returnremaining;}}}//构造方法设置初始版本号publicSemaphore(intpermits){sync=newNonfairSync(permits);}//构造方法publicSemaphore(intpermits,booleanfair){sync=fair?newFairSync(permits):newNonfairSync(permits);}//增加1publicvoidacquire()throwsInterruptedException{sync.acquireSharedInterruptibly(1);}//不间断地获取publicvoidacquireUninterruptibly(){sync.acquireShared(1);}//从此信号量获取许可,直到获得一个许可为止,将一直阻塞publicvoidacquireUninterruptibly(){sync.acquireShared(1);}//仅在调用时可用时,才从此信号量获取许可publicbooleantryAcquire(){returnsync.nonfairTryAcquireShared(1)>=0;}//定时设置publicbooleantryAcquire(longtimeout,TimeUnitunit)throwsInterruptedException{returnsync.tryAcquireSharedNanos(1,unit.toNanos(timeout));}//释放publicvoidrelease(){sync.releaseShared(1);}//设置指定的个数publicvoidacquire(intpermits)throwsInterruptedException{if(permits<0)thrownewIllegalArgumentException();sync.acquireSharedInterruptibly(permits);}//获得许可证的给定数目从这个信号,阻塞直到所有可用。publicvoidacquireUninterruptibly(intpermits){if(permits<0)thrownewIllegalArgumentException();sync.acquireShared(permits);}//获得许可证的给定数目从此信号量,只有在所有可在调用的时候。publicbooleantryAcquire(intpermits){if(permits<0)thrownewIllegalArgumentException();returnsync.nonfairTryAcquireShared(permits)>=0;}//收购从此信号量许可证的给定数量,如果所有给定的等待时间内变得可用,并且当前线程未被中断publicbooleantryAcquire(intpermits,longtimeout,TimeUnitunit)throwsInterruptedException{if(permits<0)thrownewIllegalArgumentException();returnsync.tryAcquireSharedNanos(permits,unit.toNanos(timeout));}//释放publicvoidrelease(intpermits){if(permits<0)thrownewIllegalArgumentException();sync.releaseShared(permits);}//返回现在的剩余值publicintavailablePermits(){returnsync.getPermits();}//返回减少之后的值publicintdrainPermits(){returnsync.drainPermits();}//减少指定个数的值protectedvoidreducePermits(intreduction){if(reduction<0)thrownewIllegalArgumentException();sync.reducePermits(reduction);}//判断是否时公平锁publicbooleanisFair(){returnsyncinstanceofFairSync;}//判断队列是否有线程publicfinalbooleanhasQueuedThreads(){returnsync.hasQueuedThreads();}//返回线程的长度publicfinalintgetQueueLength(){returnsync.getQueueLength();}//返回队列线程的集合protectedCollection<Thread>getQueuedThreads(){returnsync.getQueuedThreads();}//toString()方法publicStringtoString(){returnsuper.toString()+"[Permits="+sync.getPermits()+"]";}}

    总结:

    我们发现其本质还是 AbstractQueueSynchronizer 中的共享模式和独占模式

    此类也有公平和非公平的实现

    独享锁

    独享锁,也叫独占锁,意思是锁A只能被一个锁拥有,如synchronized,

    ReentrantLock是独享锁,他是基于AQS实现的,在ReentrantLock源码中, 使用一个int类型的成员变量state来表示同步状态,当state>0时表示已经获取了锁 。 而当c等于0的时候说明当前没有线程占有锁,它提供了三个方法(getState()、setState(int newState)、compareAndSetState(int expect,int update))来对同步状态state进行操作,所以AQS可以确保对state的操作是安全的。

    //它默认是非公平锁publicReentrantLock(){sync=newNonfairSync();}//创建ReentrantLock,公平锁or非公平锁publicReentrantLock(booleanfair){sync=fair?newFairSync():newNonfairSync();}//而他会分别调用lock方法和unlock方法来释放锁publicvoidlock(){sync.lock();}publicvoidunlock(){sync.release(1);}

    但是其实他不仅仅是会调用lock和unlock方法,因为我们的线程不可能一点问题没有,如果说进入到了waiting状态,在这个时候如果没有unpark()方法,就没有办法来唤醒他, 所以,也就接踵而至出现了tryLock(),tryLock(long,TimeUnit)来做一些尝试加锁或者说是超时来满足某些特定的场景的需求了

    ReentrantLock会保证method-body在同一时间只有一个线程在执行这段代码,或者说,同一时刻只有一个线程的lock方法会返回。其余线程会被挂起,直到获取锁。

    从这里我们就能看出,其实ReentrantLock实现的就是一个独占锁的功能:有且只有一个线程获取到锁,其余线程全部挂起,直到该拥有锁的线程释放锁,被挂起的线程被唤醒重新开始竞争锁。而在源码中通过AQS来获取独享锁是通过调用acquire方法,其实这个方法是阻塞的

    共享锁

    从我们之前的独享所就能看得出来,独享锁是使用的一个状态来进行锁标记的,共享锁其实也差不多,但是JAVA中有不想定力两个状态,所以区别出现了, 他们的锁状态时不一样的。

    基本的流程是一样的,主要区别在于判断锁获取的条件上,由于是共享锁,也就允许多个线程同时获取,所以同步状态的数量同时的大于1的,如果同步状态为非0,则线程就可以获取锁,只有当同步状态为0时,才说明共享数量的锁已经被全部获取,其余线程只能等待。

    最典型的就是ReentrantReadWriteLock里的读锁,它的读锁是可以被共享的,但是它的写锁确每次只能被独占。

    总结

    • 独享锁:同时只能有一个线程获得锁。

    • 共享锁:可以有多个线程同时获得锁。

    分段锁

    在JDK1.7之前,HashMap的底层是数组+链表。同样的,ConcurrentHashMap的底层树结构是数组+链表,但是和HashMap不一样的是,ConcurrentHashMap的中存放数据是一段一段的。即由很多个Segment(段)组成的,每个Segment中都有着类似于数组+链表的结构

    关于Segment

    1.ConcurrentHashMap由三个参数

    • initalCapacity:初始化总容量,默认值为16

    • loadFactor:加载因子,默认0.75

    • concurrentLevel:并发级别,默认16

    2.其中的并发级别控制了Segment的个数。在y一个ConcurrentHashMap创建后Segment的个数是不能变的,扩容过程是改变每个Segment的大小

    关于分段锁

    Segment继承了重入锁ReentrantLock,有了锁的的功能。每个锁控制的是一段,当每个Segment越来越大的时候,锁的粒度就越来越大了

    • 分段锁的优势是保证造操作不同段map的时候进行锁的竞争和等待。这相当于直接对整个map同步synchronized只是有优势的

    • 缺点在于分成很多段的时候会浪费很多的内存空间(不连续,碎片化),操作map的时候竞争一个分段锁概率狠小的时候,分段锁反而会造成更新等操作的长时间等待,分段锁的性能会下降

    JDK1.8的map实现

    JDK中的HashMap和ConcurrentHashMap。底层数据结构为数组+链表+红黑树。数组可以扩容,链表可以转化为红黑树(本篇文章不对红黑树做讲解,之前已经分析过)

    新版的ConcurrentHashMap为什么不使用ReentrantLock而使用synchronized?

    减少内存开销:如果使用ReenteantLock则需要节点继承AQS来获得同步支持,增加内存开销,而1.8中只有头节点需要同步

    内部优化:synchronized是JVM直接支持的,JVM能在运行时做出相应的优化措施:锁粗化、锁消除、锁自旋等

    锁的粒度

    首先锁的粒度并没有变粗,甚至变得更细了。每当扩容一次,ConcurrentHashMap的并发度就扩大一倍。

    Hash冲突

    JDK1.7中,ConcurrentHashMap从过二次hash的方式(Segment -> HashEntry)能够快速的找到查找的元素。在1.8中通过链表加红黑树的形式弥补了put、get时的性能差距。

    扩容

    JDK1.8中,在ConcurrentHashmap进行扩容时,其他线程可以通过检测数组中的节点决定是否对这条链表(红黑树)进行扩容,减小了扩容的粒度,提高了扩容的效率。

    死锁案例和排查

    publicclassDeadLockDemo{publicstaticvoidmain(String[]args){StringlockA="lockA";StringlockB="lockB";MyThreadmyThread1=newMyThread(lockA,lockB);MyThreadmyThread2=newMyThread(lockB,lockA);newThread(myThread1).start();newThread(myThread2).start();}}classMyThreadimplementsRunnable{privateStringlockA;privateStringlockB;publicMyThread(StringlockA,StringlockB){this.lockA=lockA;this.lockB=lockB;}@Overridepublicvoidrun(){synchronized(lockA){System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>"+lockB);try{TimeUnit.SECONDS.sleep(2);}catch(InterruptedExceptione){e.printStackTrace();}synchronized(lockB){System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"=>"+lockA);}}}}
     </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
    本文:Java多线程之锁的状态有哪些的详细内容,希望对您有所帮助,信息来源于网络。
    上一篇:Go语言中make和new有什么区别下一篇:

    3 人围观 / 0 条评论 ↓快速评论↓

    (必须)

    (必须,保密)

    阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18