Lock显式锁

基本特性

  • Lock⽅式来获取锁⽀持中断、超时不获取、是⾮阻塞的
  • 提⾼了语义化,哪⾥加锁,哪⾥解锁都得写出来
  • Lock显式锁可以给我们带来很好的灵活性,但同时我们必须⼿动释放锁
  • ⽀持Condition条件对象
  • 允许多个读线程同时访问共享资源

ReentrantLock-可重入锁

ReentrantLock 的基本使用

​ 调用 lock()方法获得锁, 调用unlock()释放锁

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
public class ReentrantLockTest {

//定义显示锁
static Lock lock = new ReentrantLock();

//定义方法
public static void test() {
//先获得锁
lock.lock();
try {
//for 循环就是同步代码块
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " -- " + i);
}
} finally {
//释放锁
lock.unlock();
}
}

public static void main(String[] args) {
Runnable r = () -> test();
//启动三个线程
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
}
}

使用 Lock 锁同步不同方法中的同步代码块

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class ReentrantLockTest02 {
static Lock lock = new ReentrantLock();

//定义锁对象
public static void test1() {
//获得锁
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "-- test 1 -- " + System.currentTimeMillis());
Thread.sleep(new Random().nextInt(1000));
System.out.println(Thread.currentThread().getName() + "-- test 1 -- " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}

public static void test2() {
//获得锁
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "-- test 2 -- " + System.currentTimeMillis());
Thread.sleep(new Random().nextInt(1000));
System.out.println(Thread.currentThread().getName() + "-- test 2 -- " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}

public static void main(String[] args) {
Runnable r1 = () -> test1();
Runnable r2 = () -> test2();
new Thread(r1).start();
new Thread(r1).start();
new Thread(r1).start();
new Thread(r2).start();
new Thread(r2).start();
new Thread(r2).start();
}
}

lockInterruptibly()方法

​ lockInterruptibly() 方法的作用:如果当前线程未被中断则获得锁,如果当前线程被中断则出现异常。

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
30
31
32
33
34
35
36
37
38
39
public class LockInterruptiblyTest {

static class Service {
private Lock lock = new ReentrantLock();

//定义锁对象
public void serviceMethod() {
try {
// 获得锁定,即使调用了线程的interrupt()方法,也没有真正的中断线程
// lock.lock();
// 如果线程被中断了,不会获得锁,会产生异常
lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + " ---> begin lock");
// 执行一段耗时的操作
for (int i = 0; i < Integer.MAX_VALUE; i++) {
new StringBuilder();
}
System.out.println(Thread.currentThread().getName() + " ---> end lock");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + " ---> 释放锁");
}
}
}

public static void main(String[] args) throws InterruptedException {
Service s = new Service();
Runnable r = () -> s.serviceMethod();
Thread t1 = new Thread(r);
t1.start();
Thread.sleep(50);
Thread t2 = new Thread(r);
t2.start();
Thread.sleep(50);
t2.interrupt(); //中断 t2 线程
}
}

​ 对于synchronized内部锁来说,如果一个线程在等待锁,只有两个结果:要么该线程获得锁继续执行;要么就保持等待。

​ 对于 ReentrantLock可重入锁来说,提供另外一种可能,在等待锁的过程中,程序可以根据需要取消对锁的请求。

通过 ReentrantLock 锁的 lockInterruptibly()方法避免死锁的产生

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
* @author D丶Cheng
* @Description: 通过 ReentrantLock 锁的 lockInterruptibly()方法避免死锁的产生
*/
public class LockInterruptiblyTest02 {
static class IntLock implements Runnable {
// 创建两个 ReentrantLock 锁对象
public static ReentrantLock lock1 = new ReentrantLock();
public static ReentrantLock lock2 = new ReentrantLock();
int lockNum;

// 定义整数变量,决定使用哪个锁
public IntLock(int lockNum) {
this.lockNum = lockNum;
}

@Override
public void run() {
try {
if (lockNum % 2 == 1) {
// 奇数,先锁 1,再锁 2
lock1.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "获得锁1,还需 要获得锁 2");
Thread.sleep(new Random().nextInt(500));
lock2.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "同时获得了锁1与锁2....");
} else {
// 偶数,先锁 2,再锁 1
lock2.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "获得锁2,还需要获得锁1");
Thread.sleep(new Random().nextInt(500));
lock1.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "同时获得了锁1与锁2....");
}
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + " 中断了线程");
} finally {
// 判断当前线程是否持有该锁
if (lock1.isHeldByCurrentThread()) {
lock1.unlock();
}
if (lock2.isHeldByCurrentThread()) {
lock2.unlock();
}
System.out.println(Thread.currentThread().getName() + "线程退出");
}
}
}

public static void main(String[] args) throws InterruptedException {
IntLock intLock1 = new IntLock(1);
IntLock intLock2 = new IntLock(2);
Thread t1 = new Thread(intLock1);
Thread t2 = new Thread(intLock2);
t1.start();
t2.start();
//在 main 线程,等待 3000 秒,如果还有线程没有结束就中断该线程
Thread.sleep(3000);
//可以中断任何一个线程来解决死锁, t2 线程会放弃对锁 1 的申请,同时释放锁 2, t1 线程会完成它的任务
if (t2.isAlive()) {
t2.interrupt();
}
}
}
// 输出
Thread-0获得锁1,还需 要获得锁2
Thread-1获得锁2,还需要获得锁1
java.lang.InterruptedException......
Thread-1中断了线程
Thread-1线程退出
Thread-0同时获得了锁1与锁2....
Thread-0线程退出

tryLock()

​ tryLock(long time, TimeUnit unit) 的作用在给定等待时长内锁没有被另外的线程持有,并且当前线程也没有被中断,则获得该锁。通过该方法可以实现锁对象的限时等待

基本使用

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
30
31
32
33
34
35
36
37
38
39
40
public class TryLockTest {
static class TimeLock implements Runnable {
private static ReentrantLock lock = new ReentrantLock();

//定义锁对象
@Override
public void run() {
try {
//获得锁返回 true
if (lock.tryLock(3, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread().getName() + "获得锁,执行耗时任务");
//假设 Thread-0 线程先持有锁,完成任务需要4秒钟,Thread-1线程尝试获得锁,
// Thread-1线程在3秒内还没有获得锁的话,Thread-1线程会放弃
// Thread.sleep(4000);
//假设Thread-0线程先持有锁,完成任务需要2秒钟,Thread-1线程尝试获得锁,
// Thread-1线程会一直尝试,在它约定尝试的3秒内可以获得锁对象
Thread.sleep(2000);
} else {
//没有获得锁
System.out.println(Thread.currentThread().getName() + "没有获得锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}

public static void main(String[] args) {
TimeLock timeLock = new TimeLock();
Thread t1 = new Thread(timeLock);
Thread t2 = new Thread(timeLock);
t1.start();
t2.start();
}
}

tryLock()仅在调用时锁定未被其他线程持有的锁,如果调用方法时,锁对象对其他线程持有,则放弃。调用方法尝试获得没,如果该锁没有被其他线程占用则返回 true 表示锁定成功;如果锁被其他线程占用则返回false,不等待。

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* @author D丶Cheng
* @Description: tryLock 测试当锁对象没有被其他线程持有的情况下才会获得该锁定
*/
public class TryLockTest02 {

static class Service {
private ReentrantLock lock = new ReentrantLock();

public void serviceMethod() {
try {
if (lock.tryLock()) {
System.out.println(Thread.currentThread().getName() + "获得锁定");
//模拟执行任务的时长
Thread.sleep(3000);
} else {
System.out.println(Thread.currentThread().getName() + "没有获得锁定 ");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}

public static void main(String[] args) throws InterruptedException {
Service service = new Service();

Runnable r = () -> service.serviceMethod();
Thread t1 = new Thread(r);
t1.start();
//睡眠 50 毫秒,确保 t1 线程锁定
Thread.sleep(50);
Thread t2 = new Thread(r);
t2.start();
}
}
// 输出
Thread-0获得锁定
Thread-1没有获得锁定

tryLock避免死锁

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* @author D丶Cheng
* @Description: tryLock 避免死锁
*/
public class TryLockTest03 {

static class IntLock implements Runnable {
private static ReentrantLock lock1 = new ReentrantLock();
private static ReentrantLock lock2 = new ReentrantLock();
private int lockNum;

//用于控制锁的顺序
public IntLock(int lockNum) {
this.lockNum = lockNum;
}

@Override
public void run() {
if (lockNum % 2 == 0) {
//偶数先锁 1,再锁 2
while (true) {
try {
if (lock1.tryLock()) {
System.out.println(Thread.currentThread().getName() + "获得 锁 1, 还想获得锁 2");
Thread.sleep(new Random().nextInt(100));
try {
if (lock2.tryLock()) {
System.out.println(Thread.currentThread().getName() + "同时获得锁 1 与锁 2 ----完成任务了");
//结束 run()方法执行,即当前线程 结束
return;
}
} finally {
if (lock2.isHeldByCurrentThread()) {
lock2.unlock();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock1.isHeldByCurrentThread()) {
lock1.unlock();
}
}
}
} else {
//奇数就先锁 2,再锁 1
while (true) {
try {
if (lock2.tryLock()) {
System.out.println(Thread.currentThread().getName() + "获得 锁 2, 还想获得锁 1");
Thread.sleep(new Random().nextInt(100));
try {
if (lock1.tryLock()) {
System.out.println(Thread.currentThread().getName() + "同时获得锁 1 与锁 2 ----完成任务了");
//结束 run()方法执行,即当前线程 结束
return;
}
} finally {
if (lock1.isHeldByCurrentThread()) {
lock1.unlock();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock2.isHeldByCurrentThread()) {
lock2.unlock();
}
}
}
}
}
}

public static void main(String[] args) {
// 运行后,使用tryLock()尝试获得锁,不会一直阻塞等待,
// 可以通过循环不停的再次尝试,如果等待的时间足够长,线程总是会获得想要的资源
IntLock intLock1 = new IntLock(1);
IntLock intLock2 = new IntLock(2);
Thread t1 = new Thread(intLock1);
Thread t2 = new Thread(intLock2);
t1.start();
t2.start();
}
}

newCondition()

​ 关键字 synchronized 与 wait()/notify()这两个方法一起使用可以实现等待/通知模式。Lock 锁newContition()方法返回Condition对象,Condition 类也可以实现等待/通知模式。使用 notify()通知时, JVM 会随机唤醒某个等待的线程。使用Condition类可以进行选择性通知

​ Condition 比较常用的两个方法:

  • await()会使当前线程等待,同时会释放锁,当其他线程调用signal()时,线程会重新获得锁并继续执行.

  • signal()用于唤醒一个等待的线程

注意:在调用Condition的await()/signal()方法前,也需要线程持有相关的Lock锁。调用await()后线程会释放这个锁,在singal()调用后会从当前Condition对象的等待队列中,唤醒 一个线程,唤醒的线程尝试获得锁, 一旦获得锁成功就继续执行。

Condition 等待与通知基本用法

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
30
31
32
33
34
35
36
37
38
39
40
41
public class ConditionTest {
static Lock lock = new ReentrantLock();
static Condition condition = lock.newCondition();

static class SubThread extends Thread {
@Override
public void run() {
lock.lock();
try {
System.out.println("condition await begin");
condition.await();
System.out.println("condition await end");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println("condition unlock");
}
}
}

public static void main(String[] args) throws InterruptedException {
SubThread t1 = new SubThread();
t1.start();
Thread.sleep(3000);
lock.lock();
try {
System.out.println("condition signal begin");
condition.signal();
System.out.println("condition signal end");
} finally {
lock.unlock();
}
}
}
// 输出
condition await begin
condition signal begin
condition signal end
condition await end
condition unlock

多个Condition实现通知部分线程, 使用更灵活

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
* @author D丶Cheng
* @Description: 多个Condition实现通知部分线程, 使用更灵活
*/
public class ConditionTest02 {
static Lock lock = new ReentrantLock();
static Condition conditionA = lock.newCondition();
static Condition conditionB = lock.newCondition();


static class Service {
public void awaitA() {
lock.lock();
try {
System.out.println("conditionA await begin");
conditionA.await();
System.out.println("conditionA await end");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void awaitB() {
lock.lock();
try {
System.out.println("conditionB await begin");
conditionB.await();
System.out.println("conditionB await end");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void signalA() {
lock.lock();
try {
conditionA.signal();
} finally {
lock.unlock();
}
}

public void signalB() {
lock.lock();
try {
conditionB.signal();
} finally {
lock.unlock();
}
}
}

public static void main(String[] args) throws InterruptedException {
Service service = new Service();
// 开启两个线程去执行await方法
new Thread(() -> service.awaitA()).start();
new Thread(() -> service.awaitB()).start();
Thread.sleep(3000);
// 只唤醒A上的等待
service.signalA();
}
}
// 输出
conditionA await begin
conditionB await begin
conditionA await end

condition实现交替打印

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class ConditionTest03 {
static Lock lock = new ReentrantLock();
static Condition condition = lock.newCondition();

public static boolean flag = true;

static class Service {
public void print1() {
lock.lock();
try {
while (flag) { //当 flag 为 true 等待
System.out.println("method1 await begin");
condition.await();
System.out.println("method1 await begin");
}
flag = true; //修改交替打印标志
System.out.println(Thread.currentThread().getName() + " -------------");
condition.signal(); //通知其他线程打印
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void print2() {
lock.lock();
try {
while (!flag) { //当 flag 为 false 等待
System.out.println("method2 await begin");
condition.await();
System.out.println("method2 await begin");
}
flag = false; //修改交替打印标志
System.out.println(Thread.currentThread().getName() + " **************");
condition.signal(); //通知其他线程打印
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

public static void main(String[] args) {
Service service = new Service();
// 开启两个线程去执行await方法
new Thread(() -> {
for (int i = 0; i < 100; i++) {
service.print1();
}
}).start();

new Thread(() -> {
for (int i = 0; i < 100; i++) {
service.print2();
}
}).start();
}
}

公平锁与非公平锁

​ 大多数情况下,锁的申请都是非公平的。如果线程1与线程2都在请求锁 A, 当锁 A 可用时, 系统只是会从阻塞队列中随机的选择一个线程,不能保证其公平性.。

​ 公平的锁会按照时间先后顺序,保证先到先得,公平锁的这一特点不会出现线程饥饿现象。

synchronized 内部锁就是非公平的。 ReentrantLock 重入锁提供了一个构造方法:ReentrantLock(boolean fair) ,当在创建锁对象时实参传递true 可以把该锁设置为公平锁。公平锁看起来很公平,但是要实现公平锁必须要求系统维护一个有序队列,公平锁的实现成本较高,性能也低。因此默认情况下锁是非公平的, 不是特别的需求,一般不使用公平锁。

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
/**
* @author D丶Cheng
* @Description: 公平锁与非公平锁
*/
public class ReentrantLockTest03 {
static Lock lock = new ReentrantLock(true);

static class SubThread extends Thread {
@Override
public void run() {
while (true) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获得了锁");
} finally {
lock.unlock();
}
}
}
}

public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new SubThread().start();
}
}
}

在上面的运行结果中可以发现:

  1. 如果是非公平锁,系统倾向于让一个线程再次获得已经持有的锁,这种分配策略是高效的,非公平的 。

  2. 如果是公平锁,多个线程不会发生同一个线程连续多次获得锁的可能,保证了公平性。

其他方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int getHoldCount(): 返回当前线程调用 lock()方法的次数 

int getQueueLength(): 返回正等待获得锁的线程预估数

int getWaitQueueLength(Condition condition): 返回与 Condition 条件相关的等待的线程预估数

boolean hasQueuedThread(Thread thread): 查询参数指定的线程是否在等待获得锁

boolean hasQueuedThreads(): 查询是否还有线程在等待获得该锁

boolean hasWaiters(Condition condition): 查询是否有线程正在等待指定的 Condition 条件

boolean isFair(): 判断是否为公平锁

boolean isHeldByCurrentThread(): 判断当前线程是否持有该锁

boolean isLocked(): 查询当前锁是否被线程持有

ReentrantReadWriteLock-读写锁

synchronized 内部锁与ReentrantLock 锁都是独占锁(排它锁)**, 同一时间只允许一个线程执行同步代码块,可以保证线程的安全性,但是执行效率低**。

ReentrantReadWriteLock读写锁是一种改进的排他锁,也可以称作共享/排他锁。允许多个线程同时读取共享数据,但是一次只允许一个线程对共享数据进行更新。

​ 读写锁通过读锁与写锁来完成读写操作。线程在读取共享数据前必须先持有读锁,该读锁可以同时被多个线程持有,即它是共享的。线程在修改共享数据前必须先持有写锁,写锁是排他的, 一个线程持有写锁时其他线程无法获得相应的锁。

​ 读锁只是在读线程之间共享,任何一个线程持有读锁时,其他线程都无法获得写锁, 保证线程在读取数据期间没有其他线程对数据进行更新,使得读线程能够读到数据的最新值,保证在读数据期间共享变量不被修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//定义读写锁 
ReadWriteLock rwLock = new ReentrantReadWriteLock();
//获得读锁
Lock readLock = rwLock.readLock();
//获得写锁
Lock writeLock = rwLock.writeLock();
//读数据
readLock.lock(); //申请读锁
try{
读取共享数据
}finally{
readLock.unlock();
}
//写数据
writeLock.lock();
//申请写锁
try{
更新修改共享数据
}finally{
writeLock.unlock();
}

​ 读写锁允许读读共享, 读写互斥,写写互斥,在JUC包中定义了ReadWriteLock接口,该接口中定义了 readLock()返回读锁,定义 writeLock()方法返回写锁。该接口的实现类是 ReentrantReadWriteLock。

注: readLock()与writeLock()**方法返回的锁对象同一个锁的两个不同的角色,不是分别获得两个不同的锁。**

读读共享

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
30
31
32
/**
* @author D丶Cheng
* @Description: ReadWriteLock 读写锁可以实现读读共享,允许多个线程同时获得读锁
*/
public class ReadWriteLockTest {

static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

public void read() {
Lock lock = readWriteLock.readLock();
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "--->获得读锁,正在读数据");
// 模拟读数据时间
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "--->读取完毕");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public static void main(String[] args) {
ReadWriteLockTest test = new ReadWriteLockTest();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
test.read();
}).start();
}
}
}

读写互斥

​ 读写锁中写锁是排他的,同一时间不允许多个线程持有同一个写锁。

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
30
31
32
/**
* @author D丶Cheng
* @Description: ReadWriteLock 读写锁写锁是互斥的,不允许多个线程同时获得写锁
*/
public class ReadWriteLockTest02 {

static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

public void write() {
Lock lock = readWriteLock.writeLock();
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "--->获得写锁,正在写数据");
// 模拟写数据时间
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "--->写数据完毕");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public static void main(String[] args) {
ReadWriteLockTest02 test = new ReadWriteLockTest02();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
test.write();
}).start();
}
}
}

​ 从运行结果可以发现,同一时间只有一个线程获得写锁。

写写互斥

写锁是独占锁,是排他锁,读线程与写线程也是互斥的。

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
* @author D丶Cheng
* @Description: ReadWriteLock演示读写互斥:
* 一个线程获得读锁时,写线程等待; 一个线程获得写锁时,其他线程等待
*/
public class ReadWriteLockTest03 {

ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
Lock readLock = readWriteLock.readLock(); // 获取读锁
Lock writeLock = readWriteLock.writeLock(); // 获取写锁

public void read() {
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "--->获得读锁,正在读数据");
// 模拟读数据时间
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "--->读取完毕");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readLock.unlock();
}
}

public void write() {
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "--->获得写锁,正在写数据");
// 模拟写数据时间
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "--->写数据完毕");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writeLock.unlock();
}
}

public static void main(String[] args) {
ReadWriteLockTest03 test = new ReadWriteLockTest03();
for (int i = 0; i < 3; i++) {
new Thread(() -> {
test.read();
}).start();
}

for (int i = 0; i < 3; i++) {
new Thread(() -> {
test.write();
}).start();
}
}
}
//输出
Thread-0--->获得读锁,正在读数据
Thread-2--->获得读锁,正在读数据
Thread-1--->获得读锁,正在读数据
Thread-0--->读取完毕
Thread-1--->读取完毕
Thread-2--->读取完毕
Thread-3--->获得写锁,正在写数据
Thread-3--->写数据完毕
Thread-4--->获得写锁,正在写数据
Thread-4--->写数据完毕
Thread-5--->获得写锁,正在写数据
Thread-5--->写数据完毕

synchronized锁和Lock锁使⽤哪个

Lock显式锁给我们的程序带来了很多的灵活性,很多特性都是Synchronized锁没有的。Lock锁在刚出来的时候很多性能⽅⾯都⽐Synchronized锁要好,但是从JDK1.6开始Synchronized锁就做了各种的优化。

  • 优化操作:适应⾃旋锁,锁消除,锁粗化,轻量级锁,偏向锁。

所以,到现在Lock锁和Synchronized锁的性能其实差别不是很⼤!⽽Synchronized锁⽤起来⼜特别简单。Lock锁还得顾忌到它的特性,要⼿动释放锁才⾏(如果忘了释放,这就是⼀个隐患)

所以说,我们绝⼤部分时候还是会使⽤Synchronized锁,⽤到了Lock锁提及的特性,带来的灵活性才会考虑使⽤Lock显式锁。