0%

线程通信

线程通信

除了使用synchronized来对代码块和方法进行同步外,jdk1.5之后还有一种Lock同步锁的方式进行同步

使用lock.lock()来进行加锁,使用lock.unlock()方法来释放锁

既然可以使用lock来代替synchronized,那么如何进行处理synchronized与wait()、notify()、notifyAll()的线程通信机制呢

在使用lock时使用Condition来进行线程通信

下面来使用两种方式来分别展示一下线程间通信

使用Object的wait()、notify进行线程通信

阻塞

调用wait()方法,将阻塞挂起等待其他线程的通知,进入该对象的线程等待池中,调用wait()方法之前,线程必须要获得该对象的对象级别锁,即只能在同步块或者同步方法中使用,否则会报IllegalMonitorStateException异常

唤醒

调用notify()方法和notifyAll()方法来通知那些等待该对象锁的其他线程,(如果有多个线程等待,notify会任意挑选一个线程来进行通知,notifyAll会对所有该对象上由于调用wait方法而被挂起的线程进行通知,),唤醒之后的线程会进入该对象的锁池中,需要竞争到对象的监视器锁才可以继续执行,调用notify()方法和notifyAll()方法之前,线程必须要获得该对象的对象级别锁,即只能在同步块或者同步方法中使用

除了调用notify()方法和notifyAll()方法来唤醒线程之外,还可以使用中断的方式来使得线程在wait()处抛出InterruptedException异常而返回并终止

如果使用的是wait(timeout)方法进行阻塞的,那么也会由于在指定时间内没有被唤醒而因为超时返回

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
/**
* wait()/notify()/notifyAll()是Object的方法
* 只能在synchronized方法或者代码块中使用,否则会报IllegalMonitorStateException异常
* 交替打印1-100
*/
public class TestThreadSignal {
public static void main(String[] args) {
PrintRunnable runnable = new PrintRunnable();
new Thread(runnable,"线程一").start();
new Thread(runnable,"线程二").start();
}
}

class PrintRunnable implements Runnable{
int num = 1;
@Override
public void run() {

while (true){
synchronized (this){
notify();// 唤醒wait的线程
if(num <= 100){
System.out.println(Thread.currentThread().getName() + "打印" + num++);
} else {
break;
}
try {
wait(); // 释放当前的锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}
}
}
虚假唤醒现象

有时在没有被其他线程调用notify/notifyAll,或者没有被中断,或者没有等待超时,也有可能会从挂起状态变为运行状态(虚假唤醒),虽然概率很低,但是最好还是使用while(条件)来一直查看条件是否满足

1
2
3
4
5
synchronized(obj){
while(条件){
obj.wait();
}
}

使用condition的await()、signal()进行线程通信

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
/**
* 使用lock和condition来进行线程通信
* condition中的await()、signal()、signalAll()方法分别对应于Object中的wait()、notify()、notifyAll()方法
* 两个线程交替打印1-100
*/
public class TestThreadSignalForLock {
public static void main(String[] args) {
PrintRunnableForLock runnable = new PrintRunnableForLock();
new Thread(runnable,"线程一").start();
new Thread(runnable,"线程二").start();
}
}

class PrintRunnableForLock implements Runnable{
int num = 1;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
@Override
public void run() {
while (true){
try{
lock.lock();
condition.signal();// 唤醒线程
if(num <= 100){
System.out.println(Thread.currentThread().getName() + "打印" + num++);
} else {
break;
}
try {
condition.await(); // 释放当前的锁
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}

}
}
}