线程交替打印

三个线程交替打印

join方法

用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行,为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。

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
package leetcode;

import sort.count;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class Main {
public static void main(String[] args) {
Thread A = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("A");
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
try {
A.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B");
}
});
Thread C = new Thread(new Runnable() {
@Override
public void run() {
try {
B.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("C");
}
});
A.start();
B.start();
C.start();
}
}

wait方法

通过设置 等待标记 flag 来记录当前拥有锁的是哪个线程, 设置 下一个标记,来记录下一个唤醒的该是哪个线程。

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
public class Main {
public static void main(String[] args) {
WaitNotify wn = new WaitNotify(1,5);// 首先把等待标记设为1, 循环次数设为5次
new Thread(() -> {
wn.print("a",1,2); // a的等待标记是1, 下一个标记是2
}).start();
new Thread(() -> {
wn.print("b",2,3); // b的等待标记是2, 下一个标记是3
}).start();
new Thread(() -> {
wn.print("c",3,1); // c的等待标记是3, 下一个标记是1
}).start();
}
}
class WaitNotify {
// 等待标记
private int flag;
// 循环次数
private int loopNumber;

public WaitNotify(int flag, int loopNumber) {
this.flag = flag;
this.loopNumber = loopNumber;
}

// 打印
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) {
e.printStackTrace();
}
}
// 获得了锁
System.out.print(str);
flag = nextFlag; // 等待标记改为下一个标记
this.notifyAll(); // 唤醒所有线程, 再与等待标记对比
}
}
}
}

交替循环输出

使用Semaphore(信号量)

有个可以控制线程启动后执行顺序,又简单的实现方式,就是用Semaphore(信号量),它可以控制共享资源的访问个数。

使用方式:

初始化的时候,指定共享资源的个数

1
2
3
// 初始化一个资源
Semaphore semaphore = new Semaphore(1);
获取资源,获取资源后,semaphore资源个数减1,变成0,其他线程再获取资源的时候就会阻塞等待
1
semaphore.acquire();

释放资源,semaphore资源个数加1,其他阻塞的线程就可以获取到资源了

1
semaphore.release();

代码为:

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
public class Main {
public static void main(String[] args) {
// 初始化三把锁,只有A锁是可用的
Semaphore A = new Semaphore(1);
Semaphore B = new Semaphore(0);
Semaphore C = new Semaphore(0);

// 创建并启动三个线程,线程1获取A锁,释放B锁
new ThreadDemo(A, B, "A").start();
// 线程2获取B锁,释放C锁
new ThreadDemo(B, C, "B").start();
// 线程3获取C锁,释放A锁
new ThreadDemo(C, A, "C").start();
}

static class ThreadDemo extends Thread {

private Semaphore current;
private Semaphore next;
private String name;

/**
* 构造方法
*
* @param current 要获取的当前锁
* @param next 要释放的下一把锁
* @param name 打印内容
*/
public ThreadDemo(Semaphore current, Semaphore next, String name) {
this.current = current;
this.next = next;
this.name = name;
}

@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
// 获取当前锁,然后打印
current.acquire();
System.out.print(name);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 释放下一把锁
next.release();
}
}
}
}

使用lock

这种情况下使用锁即可,在上锁之后进行判断 只有满足顺序的时候才打印 不然就解锁走人了 所以三个线程不管谁拿到锁,都必须是按照ABC的顺序进行打印,同时成功之后改变状态,使得顺序能够正常的执行.

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
public class ThreeThread {
private int time;// 控制打印次数
private int state;// 当前状态值:保证三个线程之间交替打印
private Lock lock = new ReentrantLock();

public ThreeThread(int time) {
this.time = time;
}

public void print(String str, int targetNum) {
for (int i = 0; i < time; ) {
lock.lock();//上锁
//只有满足顺序条件的时候才打印 并且state++和i++
if (state % 3 == targetNum) {
state++;
i++;
System.out.print(str);
}
lock.unlock();//解锁
}
}


//ABC三个线程都进入print方法 但是条件中必须满足ABC这样的顺序打印
//他们三个每个线程都有一个i 所以一共每个线程都要打印5次
public static void main(String[] args) {
ThreeThread th = new ThreeThread(5);

Thread A = new Thread(() -> {
th.print("A", 0);
});

Thread B = new Thread(() -> {
th.print("B", 1);
});

Thread C = new Thread(() -> {
th.print("C", 2);
});

B.start();
A.start();
C.start();
}
}


park & unpark 版

由于 LockSupport 的 park() 方法可以暂停当前线程,unpark() 方法可以唤醒指定的线程,所以可以在执行完当前线程中的代码后,唤醒指定的线程。

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
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

// 交替输出实现 之 park & unpark
public class Test13AlternateOutput {
static Thread t1;
static Thread t2;
static Thread t3;
public static void main(String[] args) throws InterruptedException {
ParkUnpark pu = new ParkUnpark(5); // 循环5次
t1 = new Thread(() -> {
pu.print("a",t2); // 线程1下一个要唤醒线程2
});
t2 = new Thread(() -> {
pu.print("b",t3); // 线程2下一个要唤醒线程3
});
t3 = new Thread(() -> {
pu.print("c",t1); // 线程3下一个要唤醒线程1
});
t1.start();
t2.start();
t3.start();

LockSupport.unpark(t1); // 先通过主线程唤醒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); // 唤醒指定的下一个线程
}
}
}

线程交替打印
http://example.com/2022/11/10/线程交替打印/
作者
zlw
发布于
2022年11月10日
许可协议