Synchronized VS Lock

  1. Synchronized是JVM提供的内置锁,加锁和解锁都在同一段代码块里面。(Synchronized提供加锁和内存可见性)
    Lock是一个显示锁,需要自己lock and unlock。如果没有unlock,会产生disaster。
  2. Synchronized是阻塞式的,产生死锁,只能通过重启来解决。而Lock有恢复机制,lock()方式是阻塞式的,tryLock()方法是非阻塞式的。
  3. 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; }

  1. 首先获取这个锁的状态,如果状态为0,则尝试设置状态为传入的参数(这里就是1),若设置成功就代表自己获取到了锁,返回true了。状态为0设置1的动作在外部就有做过一次,内部再一次做只是提升概率,而且这样的操作相对锁来讲不占开销。
  2. 如果状态不是0,则判定当前线程是否为排它锁的Owner,如果是Owner则尝试将状态增加acquires(也就是增加1),如果这个状态值越界,则会抛出异常提示,若没有越界,将状态设置进去后返回true(实现了类似于偏向的功能,可重入,但是无需进一步征用)。
  3. 如果状态不是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。

你可能感兴趣的:(Synchronized VS Lock)