Lock的使用

在Java多线程中,可以使用synchronized关键字来实现线程之间的同步互斥,但在JDK1.5后新增了ReetrantLock类也能达到同样的效果,并且在扩展功能上也更加强大。

1.ReentrantLock类的使用

1.1 简单使用 ReentrantLock

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
import java.util.concurrent.locks.ReentrantLock;

/**
* Created by haicheng.lhc on 17/05/2017.
*
* @author haicheng.lhc
* @date 2017/05/17
*/
public class ReentrantLockTest extends Thread {

public static ReentrantLock lock = new ReentrantLock();
public static int i = 0;

public ReentrantLockTest(String name) {
super.setName(name);
}

@Override
public void run() {
for (int j = 0; j < 10000000; j++) {
lock.lock();
try {
System.out.println(this.getName() + " " + i);
i++;
} finally {
lock.unlock();
}
}
}

/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
ReentrantLockTest test1 = new ReentrantLockTest("thread1");
ReentrantLockTest test2 = new ReentrantLockTest("thread2");

test1.start();
test2.start();
test1.join();
test2.join();
System.out.println(i);
}

最后的结果是 20000000;如果去掉锁,那么输出结果是一个小于20000000的不确定的数。

1.2 使用Conditon实现等待/通知

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
package com.jalja.org.base.Thread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
* Condition 配合Lock 实现线程的等待 与通知
*/
public class ConditionTest{
public static ReentrantLock lock=new ReentrantLock();
public static Condition condition =lock.newCondition();
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
lock.lock();//请求锁
try{
System.out.println(Thread.currentThread().getName()+"==》进入等待");
condition.await();//设置当前线程进入等待
}catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();//释放锁
}
System.out.println(Thread.currentThread().getName()+"==》继续执行");
}
}.start();
new Thread(){
@Override
public void run() {
lock.lock();//请求锁
try{
System.out.println(Thread.currentThread().getName()+"=》进入");
Thread.sleep(2000);//休息2秒
condition.signal();//随机唤醒等待队列中的一个线程
System.out.println(Thread.currentThread().getName()+"休息结束");
}catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();//释放锁
}
}
}.start();
}
}

需要注意,在调用condition.await()和condition.signal()之前必须先调用lock.lock()代码,获得同步监视器,也就是先要获得锁。

1.3 使用多个Condition实现通知部分线程

新建MyService.java:

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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package service;

 

importjava.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

importjava.util.concurrent.locks.ReentrantLock;

 

public class MyService {

 

         privateLock lock = new ReentrantLock();

         publicCondition conditionA = lock.newCondition();

         publicCondition conditionB = lock.newCondition();

 

         publicvoid awaitA() {

                   try{

                            lock.lock();

                            System.out.println("beginawaitA时间为" +System.currentTimeMillis()

                                               +" ThreadName=" + Thread.currentThread().getName());

                            conditionA.await();

                            System.out.println("  end awaitA时间为" + System.currentTimeMillis()

                                               +" ThreadName=" + Thread.currentThread().getName());

                   }catch (InterruptedException e) {

                            e.printStackTrace();

                   }finally {

                            lock.unlock();

                   }

         }

 

         publicvoid awaitB() {

                   try{

                            lock.lock();

                            System.out.println("beginawaitB时间为" +System.currentTimeMillis()

                                               +" ThreadName=" + Thread.currentThread().getName());

                            conditionB.await();

                            System.out.println("  end awaitB时间为" + System.currentTimeMillis()

                                               +" ThreadName=" + Thread.currentThread().getName());

                   }catch (InterruptedException e) {

                            e.printStackTrace();

                   }finally {

                            lock.unlock();

                   }

         }

 

         publicvoid signalAll_A() {

                   try{

                            lock.lock();

                            System.out.println("  signalAll_A时间为" + System.currentTimeMillis()

                                               +" ThreadName=" + Thread.currentThread().getName());

                            conditionA.signalAll();

                   }finally {

                            lock.unlock();

                   }

         }

 

         publicvoid signalAll_B() {

                   try{

                            lock.lock();

                            System.out.println("  signalAll_B时间为" + System.currentTimeMillis()

                                               +" ThreadName=" + Thread.currentThread().getName());

                            conditionB.signalAll();

                   }finally {

                            lock.unlock();

                   }

         }

新建ThreadA.java :

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
package extthread;

 

import service.MyService;

 

public class ThreadA extends Thread {

 

         privateMyService service;

 

         publicThreadA(MyService service) {

                   super();

                   this.service= service;

         }

 

         @Override

         publicvoid run() {

                   service.awaitA();

         }

}

新建ThreadB.java :

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
package extthread;

 

import service.MyService;

 

public class ThreadB extends Thread {

 

         privateMyService service;

 

         publicThreadB(MyService service) {

                   super();

                   this.service= service;

         }

 

         @Override

         publicvoid run() {

                   service.awaitB();

         }

}

新建Run.java :

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
package test;

 

import service.MyService;

import extthread.ThreadA;

import extthread.ThreadB;

 

public class Run {

 

         publicstatic void main(String[] args) throws InterruptedException {

 

                   MyServiceservice = new MyService();

 

                   ThreadAa = new ThreadA(service);

                   a.setName("A");

                   a.start();

 

                   ThreadBb = new ThreadB(service);

                   b.setName("B");

                   b.start();

 

                   Thread.sleep(3000);

 

                   service.signalAll_A();

         }

}

运行结果,只有线程A被唤醒了。

1.4 公平锁和非公平锁

锁Lock分为公平锁和非公平锁。公平锁表示线程获取锁的顺便是按照线程加锁的顺序来分配的,即先来先得,先进先出的顺序。而非公平锁就是一种获取锁的抢占机制,是随机获得锁的。
公平锁的顺序是大体如此,并不是100%。
在默认情况下,ReentrantLock类使用的是非公平锁。

1.5 使用Conditon可实现顺序执行

2.ReentrantReadWriteLock类的使用

ReentrantReadWriteLock类表示有两种锁,一个是读操作相关的锁,也称共享锁;另一个是写操作相关的锁,也叫排他锁。写锁与写锁互斥,写锁与读锁互斥,读锁与读锁不互斥。

读锁是长这样的:

1
2
3
4
5
6
.....
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
.....
lock.readLock().lock()
.....
lock.readLock().unlock()

写锁长这样:

1
2
3
4
5
6
.....
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
.....
lock.writeLock().lock()
.....
lock.writeLock().unlock()

使用方法和普通的Lock类似,只要记得写锁与写锁互斥,写锁与读锁互斥,读锁与读锁不互斥。

0%