Java多线程——锁
Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线程的深入剖析。
Lock锁
1、简介
1、从Java5开始,Java提供了一种功能更强大的线程同步机制——通过显式定义同步锁对象来实现同步,在这种机制下,同步锁由Lock对象充当。
2、Lock 提供了比synchronized方法和synchronized代码块更广泛的锁定操作,Lock允许实现更灵活的结构,可以具有差别很大的属性,并且支持多个相关的Condition对象。
3、Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
4、某些锁可能允许对共享资源并发访问,如ReadWriteLock(读写锁),Lock、ReadWriteLock是Java5提供的两个根接口,并为Lock 提供了ReentrantLock(可重入锁)实现类,为ReadWriteLock提供了ReentrantReadWriteLock 实现类。
5、Java8新增了新型的StampedLock类,在大多数场景中它可以替代传统的ReentrantReadWriteLock。ReentrantReadWriteLock 为读写操作提供了三种锁模式:Writing、ReadingOptimistic、Reading。
2、Lock锁使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class X{
//定义锁对象
private final ReentrantLock lock=new ReentrantLock();
//定义需要保证线程安全的方法
public void m() {
//加锁
lock.lock();
try {
//需要保证线程安全的代码
}
finally {
lock.unlock();
}
}
}
ReentranLock
1、简介
在Java多线程中,可以使用synchronized关键字来实现线程之间同步互斥,但在JDK1.5中新增加了ReentrantLock类也能达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比synchronized更加的灵活。
2、使用ReentranLock实现同步
既然ReentrantLock类在功能上相比synchronized更多,那么就以一个初步的程序示例来介绍一下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
45
46
47
48
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyService{
private Lock lock=new ReentrantLock();
public void testMethod() {
lock.lock();
for(int i=0;i<5;i++) {
System.out.println("ThreadName= "+Thread.currentThread().getName()+(" "+(i+1)));
}
lock.unlock();
}
}
class MyThread extends Thread{
private MyService service;
public MyThread(MyService service) {
this.service=service;
}
@Override
public void run() {
service.testMethod();
}
}
public class LockTest {
public static void main(String[] args) {
MyService service=new MyService();
MyThread t1=new MyThread(service);
MyThread t2=new MyThread(service);
MyThread t3=new MyThread(service);
MyThread t4=new MyThread(service);
MyThread t5=new MyThread(service);
t1.start();
t2.start();
t3.start();
t4.start();
t5.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
ThreadName= Thread-2 1
ThreadName= Thread-2 2
ThreadName= Thread-2 3
ThreadName= Thread-2 4
ThreadName= Thread-2 5
ThreadName= Thread-0 1
ThreadName= Thread-0 2
ThreadName= Thread-0 3
ThreadName= Thread-0 4
ThreadName= Thread-0 5
ThreadName= Thread-3 1
ThreadName= Thread-3 2
ThreadName= Thread-3 3
ThreadName= Thread-3 4
ThreadName= Thread-3 5
ThreadName= Thread-4 1
ThreadName= Thread-4 2
ThreadName= Thread-4 3
ThreadName= Thread-4 4
ThreadName= Thread-4 5
ThreadName= Thread-1 1
ThreadName= Thread-1 2
ThreadName= Thread-1 3
ThreadName= Thread-1 4
ThreadName= Thread-1 5
从运行的结果来看,当前线程打印完毕之后将锁进行释放,其他线程才可以继续打印。线程打印的数据是分组打印,因为当前线程已经持有锁,但线程之间打印的顺序是随机的。lock.lock()是对当前线程加锁,当线程执行完毕后调用lock.unlock()释放锁,这时候其他线程可以去获取锁,至于是哪一个线程可以争抢到锁还是看CPU的调度
3、使用Condition实现等待/通知:错误用法与解决
关键字synchronized与wait()和notify()/notifyAll()方法相结合可以实现等待/通知模式,类ReentrantLock也可以实现同样的功能,但需要借助于Condition对象。Condition类是在JDK5中出现的技术,使用它有更好的灵活性,比如可以实现多路通知功能,也就是在一个Lock对象里面可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。
在使用notify(O/notifyAll0方法进行通知时,被通知的线程却是由JVM随机选择的。但使用ReentrantLock结合Condition类是可以实现前面介绍过的“选择性通知”,这个功能是非常重要的,而且在Condition类中是默认提供的。
而synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象的身上。线程开始notifyAll()时,需要通知所有的WAITING线程,没有选择权,会出现相当大的效率问题。
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 Thread05;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyService{
private Lock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
public void await() {
try {
lock.lock();
System.out.println("A");
condition.await();
System.out.println("B");
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
System.out.println("锁释放了");
}
}
}
class MyThread extends Thread{
private MyService service;
public MyThread(MyService service) {
this.service=service;
}
@Override
public void run() {
service.await();
}
}
public class LockTest {
public static void main(String[] args) {
MyService service=new MyService();
MyThread thread=new MyThread(service);
thread.start();
}
}
输出结果:
1
A
我们可以看到输出结果只有一个A,并没有其他的输出,这是因为调用Condition的await()方法,使当前执行任务的线程进入了等待的状态
注意:在使用Condition方法时要先调用lock.lock()代码获得同步监视器
4、正确使用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
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyService{
private Lock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
public void await() {
try {
lock.lock();
System.out.println("await时间为"+System.currentTimeMillis());
condition.await();
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
System.out.println("锁释放了");
}
}
public void signal() {
try {
lock.lock();
System.out.println("signal时间为"+System.currentTimeMillis());
condition.signal();
}finally {
lock.unlock();
}
}
}
class MyThread extends Thread{
private MyService service;
public MyThread(MyService service) {
this.service=service;
}
@Override
public void run() {
service.await();
}
}
public class LockTest {
public static void main(String[] args) throws InterruptedException {
MyService service=new MyService();
MyThread thread=new MyThread(service);
thread.start();
Thread.sleep(3000);
service.signal();
}
}
运行结果:
1
2
3
await时间为1575599786039
signal时间为1575599789051
锁释放了
成功实现等待/通知模式
Object类中的wait()方法相当于Condition类中的await()方法,Object类中的wait(long timeout)方法相当于Condition类中的await(long time,TimeUnit unit)方法。Object类中的notify()方法相当于Condition类中的signal()方法。Object类中的notifyAll()方法相当于Condition类中的signalAll()方法。
5、使用多个Condition实现通知所有线程
前面使用一个Condition对象来实现等待/通知模式,其实Condition对象也可以创建多个。那么一个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
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
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyService{
private Lock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("begin awaitA时间为"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
condition.await();
System.out.println("end awaitA时间为"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB时间为"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
condition.await();
System.out.println("end awaitB时间为"+System.currentTimeMillis()+"ThreadName"+Thread.currentThread().getName());
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void signalAll() {
try {
lock.lock();
System.out.println("signalAll时间为"+System.currentTimeMillis());
condition.signalAll();
}finally {
lock.unlock();
}
}
}
class MyThreadA extends Thread{
private MyService service;
public MyThreadA(MyService service) {
this.service=service;
}
@Override
public void run() {
service.awaitA();
}
}
class MyThreadB extends Thread{
private MyService service;
public MyThreadB(MyService service) {
this.service=service;
}
@Override
public void run() {
service.awaitB();
}
}
public class LockTest {
public static void main(String[] args) throws InterruptedException {
MyService service=new MyService();
MyThreadA threadA=new MyThreadA(service);
threadA.setName("A");
threadA.start();
MyThreadB threadB=new MyThreadB(service);
threadB.setName("B");
threadB.start();
Thread.sleep(3000);
service.signalAll();
}
}
运行结果:
1
2
3
4
5
begin awaitA时间为1575600904529ThreadNameA
begin awaitB时间为1575600904545ThreadNameB
signalAll时间为1575600907537
end awaitA时间为1575600907537ThreadNameA
end awaitB时间为1575600907537ThreadNameB
6、使用多个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
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
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyService{
private Lock lock=new ReentrantLock();
private Condition conditionA=lock.newCondition();
private Condition conditionB=lock.newCondition();
public void awaitA() {
try {
lock.lock();
System.out.println("begin awaitA时间为"+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();
}
}
public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB时间为"+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();
}
}
//通知A
public void signalAll_A() {
try {
lock.lock();
System.out.println("signalAll_A时间为"+System.currentTimeMillis()+"ThreadName="+Thread.currentThread().getName());
conditionA.signalAll();
}finally {
lock.unlock();
}
}
//通知B
public void signalAll_B() {
try {
lock.lock();
System.out.println("signalAll_A时间为"+System.currentTimeMillis()+"ThreadName="+Thread.currentThread().getName());
conditionA.signalAll();
}finally {
lock.unlock();
}
}
}
class MyThreadA extends Thread{
private MyService service;
public MyThreadA(MyService service) {
this.service=service;
}
@Override
public void run() {
service.awaitA();
}
}
class MyThreadB extends Thread{
private MyService service;
public MyThreadB(MyService service) {
this.service=service;
}
@Override
public void run() {
service.awaitB();
}
}
public class LockTest {
public static void main(String[] args) throws InterruptedException {
MyService service=new MyService();
MyThreadA threadA=new MyThreadA(service);
threadA.setName("A");
threadA.start();
MyThreadB threadB=new MyThreadB(service);
threadB.setName("B");
threadB.start();
Thread.sleep(3000);
service.signalAll_A();
}
}
运行结果:
1
2
3
4
begin awaitA时间为1575601785167ThreadN