多线程之ReentrantLock

ReentrantLock类

Sychronized关键字可以来实现线程之间的同步,ReentrantLock类也可以达到同样的效果,并且在拓展功能上跟强大。

ReentrantLock的优势

ReentrantLock是一种可重入锁,它能进入已经加锁的同步代码块,不会出现阻塞自己的情况,相比Sychronized要等待到锁执行完毕,它可以中断响应,

锁类型

ReentrantLock是有两种锁的,一种是非公平锁,一种是公平锁(FairSync),默认是一种非公平锁(NonfairSync)。

1
2
3
4
5
6
7
8
//默认构造是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
//有两种锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

非公平锁

1
2
3
4
5
6
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}

非公平锁,不用等待队列中的其他线程,可以直接抢占尝试获取锁。compareAndSetState方法使用unsafe的CAS实现对状态的原子操作

公平锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
}

判断AQS中c的状态,如果hasQueuedPredecessors为空和状态等于0,表示队列中没用线程,可以尝试获取锁,利用CSA更改状态,如果状态大于
0,说明锁被获取了,然后判断获取锁的线程是否为当前线程,是则需要将 state + 1,并将值更新。

原理

ReentrantLock构造新建Sync,而sync是继承AQS(AbstractQueuedSynchronizer)的,

1
2
static final class NonfairSync extends Sync {}
abstract static class Sync extends AbstractQueuedSynchronizer {}

使用

Lock lock=new ReentrantLock();调用ReentrantLock对象的lock()方法获取锁,调用unlock()方法释放锁。 调用lock.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

public class MyService {
private Lock lock=new ReentrantLock();

public void testMethods(){
lock.lock();
for (int i = 0; i <10 ; i++) {
System.out.println("ThreadName="+Thread.currentThread().getName()+" "+(i+1));
}
lock.unlock();
}
}


public class MyThread extends Thread {
private MyService myService;

public MyThread(MyService myService) {
this.myService = myService;
}

@Override
public void run() {
myService.testMethods();
}
}

public class Run {

public static void main(String[] args) {
MyService service=new MyService();
MyThread a1 = new MyThread(service);
MyThread a2 = new MyThread(service);
MyThread a3 = new MyThread(service);
MyThread a4 = new MyThread(service);
MyThread a5 = new MyThread(service);
a1.start();
a2.start();
a3.start();
a4.start();
a5.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
ThreadName=Thread-1 1
ThreadName=Thread-1 2
ThreadName=Thread-1 3
ThreadName=Thread-1 4
ThreadName=Thread-1 5
ThreadName=Thread-1 6
ThreadName=Thread-1 7
ThreadName=Thread-1 8
ThreadName=Thread-1 9
ThreadName=Thread-1 10
ThreadName=Thread-0 1
ThreadName=Thread-0 2
ThreadName=Thread-0 3
ThreadName=Thread-0 4
ThreadName=Thread-0 5
ThreadName=Thread-0 6
ThreadName=Thread-0 7
ThreadName=Thread-0 8
ThreadName=Thread-0 9
ThreadName=Thread-0 10
ThreadName=Thread-2 1
ThreadName=Thread-2 2
ThreadName=Thread-2 3
ThreadName=Thread-2 4
ThreadName=Thread-2 5
ThreadName=Thread-2 6
ThreadName=Thread-2 7
ThreadName=Thread-2 8
ThreadName=Thread-2 9
ThreadName=Thread-2 10
ThreadName=Thread-3 1
ThreadName=Thread-3 2
ThreadName=Thread-3 3
ThreadName=Thread-3 4
ThreadName=Thread-3 5
ThreadName=Thread-3 6
ThreadName=Thread-3 7
ThreadName=Thread-3 8
ThreadName=Thread-3 9
ThreadName=Thread-3 10
ThreadName=Thread-4 1
ThreadName=Thread-4 2
ThreadName=Thread-4 3
ThreadName=Thread-4 4
ThreadName=Thread-4 5
ThreadName=Thread-4 6
ThreadName=Thread-4 7
ThreadName=Thread-4 8
ThreadName=Thread-4 9
ThreadName=Thread-4 10

由结果可知,实现了同步。

ReentrantReadWriteLock类

因为ReentrantLock.lock()方法后面的任务,这样虽然保证了实例变量的线程安全性,但是效率却是非常低下的。所有提供了一种读写锁ReentrantReadWriteLock类(也叫排他锁)

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
public class Service {
private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

public void Read(){
try {
lock.readLock().lock();
System.out.println("获取读锁"+Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.readLock().unlock();
}
}

public void Write() {
try {
lock.writeLock().lock();
System.out.println("获取写锁" + Thread.currentThread().getName() + " " + System.currentTimeMillis());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
}


public class ThreadA extends Thread{
private Service service;

public ThreadA(Service service) {
this.service = service;
}

@Override
public void run() {
service.Read();
}
}

public class ThreadB extends Thread {
private Service service;

public ThreadB(Service service) {
this.service = service;
}

@Override
public void run() {
service.Write();
}
}

public class Run {
public static void main(String[] args) {
Service service=new Service();
ThreadA threadA=new ThreadA(service);
threadA.setName("A");
threadA.start();
ThreadB threadB=new ThreadB(service);
threadB.setName("B");
threadB.start();

}
}

结果:

1
2
获取读锁A 1545462698121
获取写锁B 1545462699122

-------------本文结束感谢您的阅读-------------