重入锁:在同一个线程内可以对同一个资源锁多次,且都可以上锁成功。
能够重入的锁有synchroized和reentrantLock
一、synchroized
public class AddNum {
private Integer num = 9;
public int add(int a){
synchronized (num){
num = num+a;
}
return num;
}
public int sub(int b){
synchronized (num){
num = num - b;
}
return num;
}
public static void main(String[] args) {
AddNum addNum = new AddNum();
System.out.println(addNum.add(1)+addNum.sub(2));
}
}
javap -c AddNum.class
Compiled from "AddNum.java"
public class com.kaiyuan.p2p.uploadxw.application.AddNum {
public com.kaiyuan.p2p.uploadxw.application.AddNum();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 9
7: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
10: putfield #3 // Field num:Ljava/lang/Integer;
13: return
public int add(int);
Code:
0: aload_0
1: getfield #3 // Field num:Ljava/lang/Integer;
4: dup
5: astore_2
6: monitorenter
7: aload_0
8: aload_0
9: getfield #3 // Field num:Ljava/lang/Integer;
12: invokevirtual #4 // Method java/lang/Integer.intValue:()I
15: iload_1
16: iadd
17: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
20: putfield #3 // Field num:Ljava/lang/Integer;
23: aload_2
24: monitorexit
25: goto 33
28: astore_3
29: aload_2
30: monitorexit
31: aload_3
32: athrow
33: aload_0
34: getfield #3 // Field num:Ljava/lang/Integer;
37: invokevirtual #4 // Method java/lang/Integer.intValue:()I
40: ireturn
Exception table:
from to target type
7 25 28 any
28 31 28 any
public int sub(int);
Code:
0: aload_0
1: getfield #3 // Field num:Ljava/lang/Integer;
4: dup
5: astore_2
6: monitorenter
7: aload_0
8: aload_0
9: getfield #3 // Field num:Ljava/lang/Integer;
12: invokevirtual #4 // Method java/lang/Integer.intValue:()I
15: iload_1
16: isub
17: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
20: putfield #3 // Field num:Ljava/lang/Integer;
23: aload_2
24: monitorexit
25: goto 33
28: astore_3
29: aload_2
30: monitorexit
31: aload_3
32: athrow
33: aload_0
34: getfield #3 // Field num:Ljava/lang/Integer;
37: invokevirtual #4 // Method java/lang/Integer.intValue:()I
40: ireturn
Exception table:
from to target type
7 25 28 any
28 31 28 any
public static void main(java.lang.String[]);
Code:
0: new #5 // class com/kaiyuan/p2p/uploadxw/application/AddNum
3: dup
4: invokespecial #6 // Method "<init>":()V
7: astore_1
8: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
11: aload_1
12: iconst_1
13: invokevirtual #8 // Method add:(I)I
16: aload_1
17: iconst_2
18: invokevirtual #9 // Method sub:(I)I
21: iadd
22: invokevirtual #10 // Method java/io/PrintStream.println:(I)V
25: return
}
每一个monitorenter和monitorexit为一个锁的加锁到释放锁的过程,若能获取资源则加一,释放资源后则减一。执行多个monitorexit是为了保证锁能够被释放。
二、ReentrankLock
直接上代码
ReentrantLock lock = new ReentrantLock();
lock.lock();
底层为:ReenTrantLock
/**
*获取锁。
**<p>如果锁不是由另一个线程持有,则获取该锁并返回立即,将锁定保持计数设置为1。
**<p>如果当前线程已经持有锁,则count递增1,方法立即返回。
**<p>如果锁由另一个线程持有,则当前线程在线程调度中被禁用目的和休眠直到锁被获得,此时锁定保持计数设置为1。
*/
**
* Acquires the lock.
*
* <p>Acquires the lock if it is not held by another thread and returns
* immediately, setting the lock hold count to one.
*
* <p>If the current thread already holds the lock then the hold
* count is incremented by one and the method returns immediately.
*
* <p>If the lock is held by another thread then the
* current thread becomes disabled for thread scheduling
* purposes and lies dormant until the lock has been acquired,
* at which time the lock hold count is set to one.
*/
public void lock() {
sync.lock();
}
看下底层代码实现
接口为
/** * Performs {@link Lock#lock}. The main reason for subclassing * is to allow fast path for nonfair version. */ abstract void lock();
其实现由两种,取其中一个进行观察
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
/**
* Atomically sets synchronization state to the given updated
* value if the current state value equals the expected value.
* This operation has memory semantics of a {@code volatile} read
* and write.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that the actual
* value was not equal to the expected value.
*/
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
/**
* Sets the thread that currently owns exclusive access.
* A {@code null} argument indicates that no thread owns access.
* This method does not otherwise impose any synchronization or
* {@code volatile} field accesses.
* @param thread the owner thread
*/
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
/**
* Acquires in exclusive mode, ignoring interrupts. Implemented
* by invoking at least once {@link #tryAcquire},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquire} until success. This method can be used
* to implement method {@link Lock#lock}.
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
compareAndSetState方法中,翻译为原子地将同步状态设置为给定的已更新值(如果当前状态值等于预期值)。如果原来在当前线程中状态可以由0变成1,则将锁标记为当前的线程。否则执行acquire(int arg)方法。
acquire(int arg)的tryAcquire(arg)实现
/**
* Attempts to acquire in exclusive mode. This method should query
* if the state of the object permits it to be acquired in the
* exclusive mode, and if so to acquire it.
*
* <p>This method is always invoked by the thread performing
* acquire. If this method reports failure, the acquire method
* may queue the thread, if it is not already queued, until it is
* signalled by a release from some other thread. This can be used
* to implement method {@link Lock#tryLock()}.
*
* <p>The default
* implementation throws {@link UnsupportedOperationException}.
*
* @param arg the acquire argument. This value is always the one
* passed to an acquire method, or is the value saved on entry
* to a condition wait. The value is otherwise uninterpreted
* and can represent anything you like.
* @return {@code true} if successful. Upon success, this object has
* been acquired.
* @throws IllegalMonitorStateException if acquiring would place this
* synchronizer in an illegal state. This exception must be
* thrown in a consistent fashion for synchronization to work
* correctly.
* @throws UnsupportedOperationException if exclusive mode is not supported
*/
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
/**
* Creates and enqueues node for current thread and given mode.
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
tryAcquire(arg)抽象类中的接口有两种实现,但是最终都是执行到nonfairTryAcquire(int acquires)方法中,如果当前没有锁,先设置当前线程的锁,如果存在锁,则给status进行+1;
addWriter(Node node),如果当前锁存在尾巴节点,则在尾巴节点后保存当前线程节点,否则保存当前节点,并返回当前节点。
/*
* Various flavors of acquire, varying in exclusive/shared and
* control modes. Each is mostly the same, but annoyingly
* different. Only a little bit of factoring is possible due to
* interactions of exception mechanics (including ensuring that we
* cancel if tryAcquire throws exception) and other control, at
* least not without hurting performance too much.
*/
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
如果node的前节点是head,因为head初始化时,都是假节点,不代表有线程拥有锁,所以,再次尝试获取锁,如果获取锁,则将锁的 head 设置为当前获取锁的线程的 Node,然后返回 false。返回 false, 则代表 if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 的结果为 false,直接返回,并不需要设置中断标记。如果当前节点不是head的话,则说明该锁被别的线程占用了,那就需要等待其他线程释放该锁
意味着,对于同一个线程,可以对同一个资源加无限制次锁,但是加多少次锁就需要解多少次锁。相比之下,synchroized方法是不需要手动解锁的,可以自动解锁。