一、题目分析
需要明白这道题是考察我们对于线程的控制,所以如果没有丰富的多线程开发经验,手撕这道题是有一些难度的
写这道题之前我们首先要知道多线程的几种实现方式
多线程的实现方式
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中的参数就可以实现了
下面展示一些
// 一个线程交替实现多线程打印 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(); } }