public E take() throws InterruptedException { E x; int c = -1; final AtomicInteger count = this.count; final ReentrantLock takeLock = this.takeLock; takeLock.lockInterruptibly(); //take锁加锁 try { while (count.get() == 0) { notEmpty.await(); //如果无可用数据则一直等待,知道put()方法的通知 } x = dequeue(); c = count.getAndDecrement(); //原子操作-1 if (c > 1) notEmpty.signal(); //通知其他take方法 } finally { takeLock.unlock(); } if (c == capacity) signalNotFull(); //通知put方法 已有空间 return x; } public void put(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); // Note: convention in all put/take/etc is to preset local var // holding count negative to indicate failure unless set. int c = -1; Node<E> node = new Node<E>(e); final ReentrantLock putLock = this.putLock; final AtomicInteger count = this.count; putLock.lockInterruptibly(); try { while (count.get() == capacity) { notFull.await(); } enqueue(node); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); } finally { putLock.unlock(); } if (c == 0) signalNotEmpty(); }
锁粗化
一连串对同一个锁不停地进行请求和释放会被整合成对锁的一次操作,从而减少对锁请求同步次数
for(int i=0;i<100;i++){ synchronized(lock){ //此时把锁放到循环外边去最好 //... } }
原理:一个线程获得锁,则进入偏向模式,当这个线程再次请求锁时,无需再做任何同步操作
场景:几乎没有锁竞争的场合
操作:竞争激烈时建议关闭. -XX:+UseBiasedLocking可以开启偏向锁
原理:偏向锁失败后,会膨胀为轻量级锁,对象头部会尝试指向持有锁的线程堆栈内部,来判断是否持有对象锁,如果获得轻量级锁成功,则进入临界区,否则表示其他线程抢占到锁,当前线程膨胀为重量级锁(在膨胀前可以自旋再去尝试获得)
场景:不存在锁竞争或竞争不激烈
原理:锁膨胀后,尝试自旋,若干次后若仍得不到锁,转入重量级锁,将线程挂起
场景:竞争不激烈,且持有锁时间较短
原理:去除不可能存在共享的资源竞争锁,如某些jdk内部自带的类
场景:单线程下的加锁操作会被锁消除
逃逸分析:观察某变量是否会逃逸出某个作用域,如果该变量没有逃逸出该作用域,则虚拟机会把该变量内部的锁消除掉
操作:-XX:+DoEscapeAnalysis 打开逃逸分析; -XX:+EliminateLocks 打开锁消除