synchronized实现原理

    技术2026-03-30  14

    synchronized实现原理

    我们先以synchronized来写一个死锁,来聊它的实现原理

    DSynchroinzed :

    public class DSynchroinzed { String locka = "locka"; String lockb = "lockb"; public static void main(String[] args) { final DSynchroinzed dSynchroinzed = new DSynchroinzed(); new Thread(new Runnable() { @Override public void run() { dSynchroinzed.AMethod(); } }).start(); new Thread(new Runnable() { @Override public void run() { dSynchroinzed.BMethod(); } }).start(); } //同步方法块,锁住的是括号里面的对象 public void AMethod(){ synchronized (locka){ for (int i = 1; i < 3; i++) { System.err.println("AMethod------>"); try { Thread.sleep(3000); synchronized (lockb){} } catch (InterruptedException e) { e.printStackTrace(); } } } } public void BMethod(){ synchronized (lockb){ for (int i = 1; i < 3; i++) { System.err.println("BMethod------->"); try { Thread.sleep(3000); synchronized (locka){} } catch (InterruptedException e) { e.printStackTrace(); } } } } }

    输出:

    AMethod------> BMethod------->

    通过上面我们可以看到,当a线程拿到了锁,循环执行的过程中,啪唧一下把lockb也上锁了,而b线程拿到了锁之后,在循环执行的过程中,啪唧一下把locka也锁了,那么,当他俩通过了一轮循环,在等待了三秒之后,开始执行下一个线程的时候,就会出现问题了,对方的锁已经把自己要跑的线程锁住了,都在等待对方释放线程,那么这种情况就会出现了死锁

    下面我们聊聊synchronized的实现原理 在synchronized内部有contentionList、entryList、waitSet、onDeck、owner、!owner这六个区域,每个区域都代表了不同的状态 contentionList:锁竞争队列,所有的请求锁的线程,都会先在这个队列中 entryList:竞争候选列表,在contentionList中有资格成为候选者来竞争锁资源的线程就被移动了这个区域内 waitSet:等待集合,调用wait方法后被阻塞的线程将被放在waitSet中 onDeck:竞争候选者,在同一个时刻最多只有一个线程在竞争锁资源,该线程的状态被称为onDeck owner:竞争到锁资源的线程 !owner:在竞争到锁资源的线程释放锁后,会从owner状态变为!owner状态 synchronized在收到信的锁请求时首先会自旋(先循环等待,每个一段时间去拿一下),如果通过自旋还是没有获得锁资源,则放入竞争队列中(contentionList) 为了防止锁竞争时contentionList尾部的元素被大量的并发线程进行CAS访问而影响性能,owner线程会在释放锁资源时将contentionList中的部分线程移动到entryList中,并指定entryList中的某个线程(一般是先进入的线程)为onDeck,owner线程并没有把锁直接给onDeck,而是把锁的权利交给了onDeck,onDeck里面的线程重新竞争锁 获得锁资源的onDeck线程会变为owner线程,而未获得锁资源的线程则留在了entryList中, owner线程被wait方法阻塞后,会被转移到waitSet队列中,直到被notify方法或者notifyall方法唤醒,被再次进入到entryList中,要注意的是:contentionList、entryList、waitSet中的线程都为阻塞状态,该阻塞状态是由操作系统来完成的 在synchronized中,在现场进入contentionList之前,等待的线程会尝试以自旋的方式获取锁,如果获取不到就进入contentionList,该做法对于已经进入contentionList中的线程是不公平的(都在厕所排队,你非要插队,那别人拉裤裆了咋办),因此synchronized是非公平锁,另外自旋获取锁的线程也可以直接抢占onDeck线程的锁资源。 synchronized是一个重量级操作,需要调用操作系统中的相关接口,性能较低,给线程加锁的时间有可能超过获取锁后的具体逻辑代码的操作时间

    喜欢的小伙伴可以加我微信公众号,每天都有各种干货分享的

    Processed: 0.010, SQL: 9