一、题目分析
需要明白这道题是考察我们对于线程的控制,所以如果没有丰富的多线程开发经验,手撕这道题是有一些难度的
写这道题之前我们首先要知道多线程的几种实现方式
多线程的实现方式
thread内置方法 Or 实现Runnable,使用ReetrantLock同步锁解决
最为粗暴的一种,直接new Thread(() -> {}),内部使用逻辑实现线程的运行方法,假设我们给这道题设置一个ABC的循环次数100,我们知道每个线程的暂停是通过ReetrantLock锁的lock方法执行同步,然后线程调用wait进入阻塞状态,可以让其他的线程获取lock资源执行;如果发现是想要打印对应字母的线程,需要执行sout输出对应字母,然后调用notifyAll()唤醒其他线程。
注意:上面唤醒线程需要全部唤醒(假设线程数量有3个的话),因此多出来唤醒的部分我们需要用if / while条件对其进行阻塞,这样大体的框架就出来了
// An highlighted block
```
// A code block
var foo = ‘bar’;
```javascript
// An highlighted block
public class MultiThreadPrint123 {
public static int times = 0;
private static Object lock = new Object();
public static void main(String[] args) {
Thread threadA = new Thread(new Thread1());
Thread threadB = new Thread(new Thread2());
Thread threadC = new Thread(new Thread3());
threadA.start();
threadB.start();
threadC.start();
}
static class Thread1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (lock) {
try {
while (times % 3 != 0) {
lock.wait();
}
System.out.println("A");
times += 1;
lock.notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
static class Thread2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (lock) {
try {
while (times % 3 != 1) {
lock.wait();
}
System.out.println("B");
times += 1;
lock.notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
static class Thread3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (lock) {
try {
while (times % 3 != 2) {
lock.wait();
}
System.out.println("C");
times += 1;
lock.notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
上面展示了一个用三个线程循环打印ABC的代码,但是如果是循环打印ABCDEFG,需要重复写7、8个线程吗?其实不用,我们可以有更简单的方式实现线程打印,增加Thread中的参数就可以实现了
下面展示一个使用只需要定义一个Thread加参数的方式,传入ABC,打印线程的方法
// 一个线程交替实现多线程打印
public class MultiThreadSimple {
//将三个线程简化为一个线程,循环打印ABC
private static Object lock = new Object();
private static int cnt = 0;
static class ThreadSimple implements Runnable {
private int num;
private String strPrint;
public ThreadSimple(int num, String strPrint) {
this.num = num;
this.strPrint = strPrint;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (lock) {
try {
while (cnt % 3 != num) {
lock.wait();
}
System.out.println(strPrint); // 使用参数
cnt += 1;
lock.notifyAll();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
new Thread(new ThreadSimple(0,"A") ).start();
new Thread(new ThreadSimple(1,"B") ).start();
new Thread(new ThreadSimple(2,"C") ).start();
}
}
Semaphore的信号量acquire和release实现
话不多说,直接上代码
// Semaphore实现交替打印ABC
public class SemaphoreTest {
private static Semaphore semaphore1 = new Semaphore(1);
private static Semaphore semaphore2 = new Semaphore(0);
private static Semaphore semaphore3 = new Semaphore(0);
public static void printABC(String str,Semaphore now, Semaphore next){
for(int i=0 ;i< 10; i++){
try{
now.acquire();
System.out.println(str);
next.release();
}catch (Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new Thread(() -> printABC("A",semaphore1, semaphore2)).start();
new Thread(() -> printABC("B",semaphore2, semaphore3)).start();
new Thread(() -> printABC("C",semaphore3, semaphore1)).start();
}
}
可以看出,这种方式比thread+reetrantLock更加简单
Condition的实现方式
实现方式如下,可以看到核心方法为将wait和notifyAll转换为await和signal,好处是可以做特定线程的唤醒。最后因为增加的是reetrantLock锁,所以要在finally代码块中对lock对象进行解锁
// A code block
public class MultiConditionPrintABC {
private static Lock lock = new ReentrantLock();
private static Condition conditionA = lock.newCondition();
private static Condition conditionB = lock.newCondition();
private static Condition conditionC = lock.newCondition();
private static int state = 0;
static class ThreadA implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
lock.lock();
try {
while (state % 3 != 0) {
conditionA.await();
}
System.out.print("A");
state++;
conditionB.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
static class ThreadB implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
lock.lock();
try {
while (state % 3 != 1) {
conditionB.await();
}
System.out.print("B");
state++;
conditionC.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
static class ThreadC implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
lock.lock();
try {
while (state % 3 != 2) {
conditionC.await();
}
System.out.print("C");
state++;
conditionA.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
public static void main(String[] args) {
new Thread(new ThreadA()).start();
new Thread(new ThreadB()).start();
new Thread(new ThreadC()).start();
}
}