问题:
按顺序打印字母。如abcabcabcabcabc,即将字母abc按顺序打印5次。
思路:
设置3个线程,分别打印a,b,c,设置1个标志位flag,表示当前该打印哪个字母:
- 如果线程要打印的字母的标志位和该标志相等,打印字母、更新flag、唤醒等待线程;
- 否则线程等待,直到被唤醒。
参考代码
/**
* 按顺序打印字母a,b,c,打印结果为abcabcabcabcabc
* **/
/**
* 思路:1)设置3个线程,分别打印字母 a,b,c;
* 2)控制线程的打印顺序:变量nowFlag:表示正在打印的字母;变量
* nextFlag表示下一次该打印哪个字母了(即唤醒哪个线程)。
* nowFlag nextFlag
* 1 表示要打印a 2 表示下次打印b
* 2 表示要打印b 3 表示下次打印c
* 3 表示要打印c 1 表示下次打印a
* 3)线程打印字母完成后修改变量,并唤醒等待的线程
* **/
class PrintStr{
//要打印的字符
private int flag;
//循环次数
private int loopNumber;
public PrintStr(int flag,int loopNumber){
this.flag = flag;
this.loopNumber = loopNumber;
}
//打印方法
public void print(char c, int nowFlag, int nextFlag){
for(int i = 0; i < loopNumber; i++){
synchronized(this){
//当前要打印的字母 不等于 当前线程要打印的字母,线程等待
//用while,防止虚假唤醒
while(flag != nowFlag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//打印字符
System.out.print(c);
//修改变量
flag = nextFlag;
//唤醒其他等待线程
this.notifyAll();
}
}
}
}
public class Print{
public static void main(String[] args){
int loopNumber = 5;
int flag = 1;
PrintStr p = new PrintStr(flag,loopNumber);//从a开始打印,循环打印5次
//打印字母a
new Thread(()->{
p.print('a',1,2);
}).start();
//打印字母b
new Thread(()->{
p.print('b',2,3);
}).start();
//打印字母c
new Thread(()->{
p.print('c',3,1);
}).start();
}
}
小结
如果涉及到2个字母,可以用一个boolean类型的flag来标识当前要打印的字母。比如打印ab:flag为true时打印字母a,flag为false时打印b。但是涉及到2个以上的字母就用上面的方法。
利用条件变量解决问题
思路
创建3个条件变量c1、c2、c3,打印字母a、b、c的线程在他们各自对应的条件变量上,开始时3个线程都在自己的条件变量上等待,然后主线程唤醒c1上的线程,打印字母a,打印完成后唤醒下一个条件变量上的线程c2。
参考代码
public class Test{
public static void main(String[] args)throws InterruptedException{
Lock lock = new ReentrantLock();
AwaitSignal a = new AwaitSignal(5, lock);
//创建条件变量,c1、c2、c3,分别表示打印字母a、b、c的条件变量
Condition c1 = lock .newCondition();
Condition c2 = lock .newCondition();
Condition c3 = lock .newCondition();
//创建3个线程,分别打印a、b、c
new Thread(()->{
a.print('a',c1,c2);
}).start();
new Thread(()->{
a.print('b',c2,c3);
}).start();
new Thread(()->{
a.print('c',c3,c1);
}).start();
//先打印字符a
Thread.sleep(10);
lock.lock();
try{
//唤醒c1
c1.signal();
}cache(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
class AwaitSignal{
private int loopNumber;
private Lock lock;
public AwaitSignal(int loopNumber, Lock lock){this.loopNumber = loopNumber;this.lock = lock;}
//current代表 当前条件变量,next 代表 下一个要唤醒的条件变量
public void print(char c,Condition current,Condition next){
for(int i = 0; i < loopNumber; i++){
lock.lock();
try{
current.await();
/** 唤醒后执行的操作 **/
//1.打印字符
System.out.print(c);
//2.唤醒其他线程
next.signal();
}cache(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
}
小结
在加锁时的代码形式:
lock();
try{…}
cache(Exception e){ e.printStackTrace();}
finally{ unlock(); }
利用park/unpark解决问题
参考代码
public class Test{
static Thread t1;
static Thread t2;
static Thread t3;
public static void main(String[] args){
Print p = new Print(5);
t1 = new Thread(()->{
p.print('a',t2);
});
t2 = new Thread(()->{
p.print('b',t3);
});
t3 = new Thread(()->{
p.print('c',t1);
});
t1.start();
t2.start();
t3.start();
//唤醒线程1
LockSupport.unpark(t1);
}
}
class Print{
private int loopNumber;
public Print(int loopNumber){this.loopNumber = loopNumber;}
public void print(char c,Thread next){
for(int i = 0; i < loopNumber; i++){
//线程等待
LockSupport.park();
//打印字母
System.out.print(c);
//唤醒下一个线程
LockSupport.unpark(next);
}
}
}