- Synchronized是JVM提供的内置锁,加锁和解锁都在同一段代码块里面。(Synchronized提供加锁和内存可见性)
Lock是一个显示锁,需要自己lock and unlock。如果没有unlock,会产生disaster。 - Synchronized是阻塞式的,产生死锁,只能通过重启来解决。而Lock有恢复机制,lock()方式是阻塞式的,tryLock()方法是非阻塞式的。
- Lock在加锁和内存上提供的语义与内置锁相同。但是提供其他功能
可定时的,可轮询的与可中断的锁获取方式,以及公平队列,非块结构的锁。
AQS
AbstractQueuedSynchronizer
AQS如何实现阻塞的
lock加锁方式阻塞式加锁,是如何实现的呢?
final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
间接调用
final boolean acquireQueued(final Node node, int arg) { try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && //This method block current thread. Details is below: parkAndCheckInterrupt()) interrupted = true; } } catch (RuntimeException ex) { cancelAcquire(node); throw ex; } }
Lock间接调用到parkAndCheckInterrupt() method。这个方法block current thread and set the interrupted status true
* Convenience method to park and then check if interrupted
*
* @return {@code true} if interrupted
*/
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
Lock独占锁实现方式
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
- 首先获取这个锁的状态,如果状态为0,则尝试设置状态为传入的参数(这里就是1),若设置成功就代表自己获取到了锁,返回true了。状态为0设置1的动作在外部就有做过一次,内部再一次做只是提升概率,而且这样的操作相对锁来讲不占开销。
- 如果状态不是0,则判定当前线程是否为排它锁的Owner,如果是Owner则尝试将状态增加acquires(也就是增加1),如果这个状态值越界,则会抛出异常提示,若没有越界,将状态设置进去后返回true(实现了类似于偏向的功能,
可重入
,但是无需进一步征用
)。 - 如果状态不是0,且自身不是owner,则返回false。
CAS核心
CAS不需要加锁,在某些条件下,性能很好;但是某些情况下,性能未必好
如AtomicInteger incrementAndGet, for( ; ;) 是无限循环,
如果线程很多,compareAndSet方法成功概率低的时候,性能未必好。
public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } }
Class sun.misc.Unsafe
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
这几个native方法,是原子操作
是利用我们将其交给硬件—CPU和内存,利用CPU的多处理能力,实现硬件层面的阻塞
再加上volatile变量的特性即可实现基于原子操作的线程安全。
所以说,CAS并不是无阻塞,只是阻塞并非在语言、线程方面,而是在硬件层面,所以无疑这样的操作会更快更高效!
什么时候使用Lock:
当需要获取可定时的,可轮询的与可中断的锁获取方式,以及公平队列,非块结构的锁。否则,还是优先使用Synchronized。