在上一个帖子中简单的表示了两个线程之间的通信,但如果有三个线程或以上还能用类似的方法吗?通过再增加一个print3
的方法,再加一个线程来让三个线程顺序执行呢,直接说结果:不行!
因为notify()
方法是随机唤醒一个等待中的线程,当有两个线程在等待的时候,只会随机的唤醒其中的一个,被唤醒的线程就会绕过判断flag
变量的判断,执行其他的顺序了,那么三个线程肯定不能的顺序执行了。
这时候就要用另一个唤醒方法了notifyAll()
,此方法会唤醒所有在等待的线程,Printer
类里print()
方法内的flag
变量判断从if
改为while
,因为等待的线程是随机被唤醒的,如果flag
变量不匹配方法就应该继续是等待的状态。
public class Test {
public static void main(String[] args) {
final Printer printer = new Printer();
new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
printer.print1();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
printer.print2();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
printer.print3();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}.start();
}
}
class Printer {
private int flag = 1;
public void print1() throws Exception {
synchronized(this) {
while (flag != 1) {
this.wait();
}
System.out.println(Thread.currentThread().getName() + " : 11111");
flag = 2;
this.notifyAll();
}
}
public void print2() throws Exception {
synchronized(this) {
while (flag != 2) {
this.wait();
}
System.out.println(Thread.currentThread().getName() + " : 22222");
flag = 3;
this.notifyAll();
}
}
public void print3() throws Exception {
synchronized(this) {
while (flag != 3) {
this.wait();
}
System.out.println(Thread.currentThread().getName() + " : 33333");
flag = 1;
this.notifyAll();
}
}
}
JDK1.5的新特性互斥锁
问题来了:这样做岂不是麻烦的很?
在1.5版本后有个新特征,叫互斥锁ReentrantLock
,可以唤醒指定的线程。
用法:
- (1)同步
- 使用
ReentrantLock
类的lock()
和unlock()
方法进行同步
- 使用
- (2)通信
- 使用
ReentrantLock
类的newCondition()
方法可以获取Condition
对象。 - 需要等待的时候使用
Condition
的await()
方法, 唤醒的时候用signal()
方法。 - 不同的线程使用不同的
Condition
, 这样就能区分唤醒的时候找哪个线程了。
- 使用
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Test {
public static void main(String[] args) {
final Printer printer = new Printer();
new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
printer.print1();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
printer.print2();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
printer.print3();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}.start();
}
}
class Printer {
private ReentrantLock r = new ReentrantLock();
private Condition c1 = r.newCondition();
private Condition c2 = r.newCondition();
private Condition c3 = r.newCondition();
private int flag = 1;
public void print1() throws Exception {
r.lock();
if (flag != 1) {
c1.await();
}
System.out.println(Thread.currentThread().getName() + " : 11111");
flag = 2;
c2.signal();
r.unlock();
}
public void print2() throws Exception {
r.lock();
if (flag != 2) {
c2.await();
}
System.out.println(Thread.currentThread().getName() + " : 22222");
flag = 3;
c3.signal();
r.unlock();
}
public void print3() throws Exception {
r.lock();
if (flag != 3) {
c3.await();
}
System.out.println(Thread.currentThread().getName() + " : 33333");
flag = 1;
c1.signal();
r.unlock();
}
}
因为可以调用指定的Condition
对象唤醒线程,所以可以把while
改成if
。