一、固定顺序运行
固定顺序运行的意思就是永远都是优先执行 t2 线程,等到 t2 线程执行完毕之后才可以执行 t1 线程,常见的实现方式有三种,下面分别介绍下。
1.1 使用 wait¬ify
可以使用 wait¬ify 来实现指定线程顺序运行的效果,如下面的代码,
@Slf4j(topic = "c.test")
public class Main {
static Object lock = new Object();
static boolean status = false;
public static void main(String[] args) {
Thread t1 = new Thread(() ->{
synchronized (lock){
while(!status){
try {
lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
log.debug("1");
}
},"t1");
Thread t2 = new Thread(() ->{
synchronized (lock){
status = true;
log.debug("2");
lock.notify();
}
},"t2");
t1.start();
t2.start();
}
}
1.2 使用 await&signal
也可以使用 ReentrantLock 类 Condition 的 await&signal 方法来实现,代码如下:
@Slf4j(topic = "c.test")
public class Main2 {
static Lock lock = new ReentrantLock();
static boolean status = false;
public static void main(String[] args) {
Condition condition = lock.newCondition();
Thread t1 = new Thread(() -> {
lock.lock();
try {
while (!status) {
try {
condition.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
} finally {
lock.unlock();
}
log.debug("1");
}, "t1");
Thread t2 = new Thread(() -> {
lock.lock();
try {
status = true;
log.debug("2");
condition.signal();
} finally {
lock.unlock();
}
}, "t2");
t1.start();
t2.start();
}
}
1.3 使用 park&unpark
还可以使用 LockSupport 类的 park&unpark 方法来实现这种效果,代码如下:
@Slf4j(topic = "c.test")
public class Main3 {
public static void main(String[] args) {
Thread t1 = new Thread(() ->{
LockSupport.park();
log.debug("1");
},"t1");
Thread t2 = new Thread(() ->{
log.debug("2");
LockSupport.unpark(t1);
},"t2");
t1.start();
t2.start();
}
}
二、交叉顺序运行
交叉顺序运行的意思就是几个线程之间按照约定的顺序依次执行。需求描述:有三个线程,线程 t1 只能打印 a,线程 t2 只能打印 b,线程 t3 只能打印 c,要求打印出字符串 abcabcabcabcabc ,即按照 abc 的顺序,循环打印五次。
2.1 使用 wait¬ify
@Slf4j(topic = "c.test")
public class Main4 {
public static void main(String[] args) {
SynWaitNotify swn = new SynWaitNotify(1, 5);
new Thread(() ->{
swn.print("a",1,2);
},"t1").start();
new Thread(() ->{
swn.print("b",2,3);
},"t2").start();
new Thread(() ->{
swn.print("c",3,1);
},"t3").start();
}
}
class SynWaitNotify{
// 表示当前的打印
private int flag;
// 表示循环打印的次数
private int loopNumber;
public SynWaitNotify(int flag,int loopNumber){
this.flag = flag;
this.loopNumber = loopNumber;
}
// 方法有三个参数,第一个参数是当前要打印的字符串,第二个参数为当前字符串的标识位,第三个参数为下一次要打印的标识位
// a 1
// b 2
// c 3
public void print(String str,int waitFlag,int nextFlag){
for(int i=0;i<loopNumber;i++){
synchronized (this){
while(flag != waitFlag){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.print(str);
flag = nextFlag;
this.notifyAll();
}
}
}
}
2.2 使用 await&signal
此种方式的解决思路为,先创建三个条件变量,让他们分别到自己的房间里面去等待,然后让主线程先唤醒 a 的条件变量,然后依次顺序执行起来,代码如下:
@Slf4j(topic = "c.test")
public class Main5 {
public static void main(String[] args) throws InterruptedException {
AwaitSignal awaitSignal = new AwaitSignal(5);
Condition a = awaitSignal.newCondition();
Condition b = awaitSignal.newCondition();
Condition c = awaitSignal.newCondition();
new Thread(() ->{
awaitSignal.print("a",a,b);
}).start();
new Thread(() ->{
awaitSignal.print("b",b,c);
}).start();
new Thread(() ->{
awaitSignal.print("c",c,a);
}).start();
try{
awaitSignal.lock();
Thread.sleep(1000);
System.out.println("开始。。。。。");
a.signal();
}finally {
awaitSignal.unlock();
}
}
}
class AwaitSignal extends ReentrantLock{
private int loopNumber;
public AwaitSignal(int loopNumber){
this.loopNumber = loopNumber;
}
public void print(String str, Condition current,Condition next){
for(int i=0;i<loopNumber;i++){
try{
lock();
try {
current.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.print(str);
next.signal();
}finally {
unlock();
}
}
}
}
2.3 使用 park&unpark
这种方式的思想就是先把三个子线程都阻塞住,然后让主线程唤醒一个线程,然后依次唤醒后续的线程,代码如下:
@Slf4j(topic = "c.test")
public class Main6 {
static Thread t1;
static Thread t2;
static Thread t3;
public static void main(String[] args) {
ParkUnpark pu = new ParkUnpark(5);
t1 = new Thread(() ->{
pu.print("a",t2);
});
t2 = new Thread(() ->{
pu.print("b",t3);
});
t3 = new Thread(() ->{
pu.print("c",t1);
});
t1.start();
t2.start();
t3.start();
LockSupport.unpark(t1);
}
}
class ParkUnpark{
private int loopNumber;
public ParkUnpark(int loopNumber){
this.loopNumber = loopNumber;
}
public void print(String str,Thread next){
for(int i=0;i<loopNumber;i++){
LockSupport.park();
System.out.print(str);
LockSupport.unpark(next);
}
}
}