JUC包一些锁的介绍和使用

JUC包一些锁的介绍和使用

十月 29, 2020

JUC包的这些锁都是基于AQS的实现,关于AQS还请看下一篇文章

一、ReentrantLock可重入锁

独占锁,synchronized就属于可重入锁。
Lock可以替代synchronized锁代码块。

1
2
3
4
5
6
7
8
9
public void test() {
synchronized (this) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

上面代码等价于:

1
2
3
4
5
6
7
8
9
10
11
12
13
Lock lock = new ReentrantLock();
public void test() {
try {
lock.lock();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//一定要在finally里面解锁,不然就可能死锁了
lock.unlock();
}

}

lock的其他优点。

1.tryLock.尝试的获取锁

tryLock拿到锁返回true,否则false。

1
2
3
4
//无参数
lock.tryLock();
//有参数,用五秒获取锁
lock.tryLock(5, TimeUnit.SECONDS);

2.可以设置Lock是公平锁

公平锁指的是谁先来谁就用。
在new的时候指定。

1
Lock lock = new Lock(true);

二、CountDownLatch

共享锁,倒计时,当latch不为0的时候一直阻塞,为0才继续。

1
2
3
CountDownLatch latch = new CountDownLatch(50);//从50开始倒计时
latch.countDown();//计时器减一
latch.await();//直至减到0时候都在阻塞

底层的countDown已经用CAS保证了原子性了。下面是jdk8源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}

三、CyclistBarrier

共享锁,每次await都会增加一个barrier,当达到阈值之后,会执行barrier的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CyclicBarrier barrier = new CyclicBarrier(20, new Runnable() {
public void run() {
System.out.println("go");
}
});
public void test() {
for (int i = 0 ; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
).start();
}
//输出结果是五行go
}

四、ReadWriteLock

共享锁,读锁在上锁时其他读线程可以继续读,而写线程只能等着;相反,要是写线程先上锁,读线程只能等着,而且同时只能有一个线程写。

1
2
3
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
Lock readLock = readWriteLock.readLock();
Lock writeLock = readWriteLock.writeLock();

五、信号量Semaphore

共享锁,信号量表示同一时刻能有多少线程获取到这把锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
Semaphore semaphore = new Semaphore(1);//参数是允许的数量
public void test() {
try {
semaphore.acquire();//acquire让数量减一,必须大于0才能acquire,否则就阻塞
Thread.sleep(1000);

} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();//release让数量加一
}

}