面试题|面试题 - 使用线程交替打印奇数偶数

【面试题|面试题 - 使用线程交替打印奇数偶数】这世上有三样东西是别人抢不走的:一是吃进胃里的食物,二是藏在心中的梦想,三是读进大脑的书
  • 分析题目。需要使用两个线程交替打印奇偶数。
    • 使用同步锁解决这个问题
    • 使用信号量来实现交替打印
  • 定义两个信号量,一个奇数信号量,一个偶数信号量,都初始化为1
  • 先用掉偶数的信号量,因为要让奇数先启动,等奇数打印完再释放
信号量实现
  • 具体实现思路:
    • 定义两个信号量,一个奇数信号量,一个偶数信号量,都初始化为1
    • 先用掉偶数的信号量,因为要让奇数先启动,等奇数打印完再释放
    • 具体流程就是 第一次的时候先减掉偶数的信号量 奇数线程打印完成以后用掉奇数的信号量。然后释放偶数的信号量如此循环
import java.util.concurrent.Semaphore; /** * @ClassName AlternatePrinting * @Author yunlogn * @Date 2019/5/21 * @Description 交替打印奇偶数 */ public class AlternatePrinting { static int i = 0; public static void main(String[] args) throws InterruptedException {Semaphore semaphoreOdd = new Semaphore(1); Semaphore semaphoreEven = new Semaphore(1); semaphoreOdd.acquire(); //让奇数先等待启动,所以先减掉偶数的信号量 等奇数线程来释放SemaphorePrintEven semaphorePrintEven = new SemaphorePrintEven(semaphoreOdd, semaphoreEven); Thread t1 = new Thread(semaphorePrintEven); t1.start(); SemaphorePrintOdd semaphorePrintOdd = new SemaphorePrintOdd(semaphoreOdd, semaphoreEven); Thread t2 = new Thread(semaphorePrintOdd); t2.start(); } /** * 使用信号量实现 */ static class SemaphorePrintOdd implements Runnable {private Semaphore semaphoreOdd; private Semaphore semaphoreEven; public SemaphorePrintOdd(Semaphore semaphoreOdd, Semaphore semaphoreEven) { this.semaphoreOdd = semaphoreOdd; this.semaphoreEven = semaphoreEven; }@Override public void run() { try {semaphoreOdd.acquire(); //获取信号量 semaphoreOdd在初始化的时候被获取了信号量所以这里被阻塞了,所以会先执行下面的奇数线程 while (true) { i++; if (i % 2 == 0) { System.out.println("偶数线程:" + i); semaphoreEven.release(); //释放偶数信号量 让奇数线程那边的阻塞解除 //再次申请获取偶数信号量,因为之前已经获取过,如果没有奇数线程去释放,那么就会一直阻塞在这,等待奇数线程释放 semaphoreOdd.acquire(); } } } catch (InterruptedException e) { e.printStackTrace(); } } } static class SemaphorePrintEven implements Runnable {private Semaphore semaphoreOdd; private Semaphore semaphoreEven; public SemaphorePrintEven(Semaphore semaphoreOdd, Semaphore semaphoreEven) { this.semaphoreOdd = semaphoreOdd; this.semaphoreEven = semaphoreEven; }@Override public void run() {try {semaphoreEven.acquire(); while (true) { i++; if (i % 2 == 1) { System.out.println("奇数线程:" + i); semaphoreOdd.release(); //释放奇数信号量 让偶数线程那边的阻塞解除//这里阻塞,等待偶数线程释放信号量 //再次申请获取奇数信号量,需要等偶数线程执行完然后释放该信号量,不然阻塞 semaphoreEven.acquire(); } }} catch (Exception ex) {}} } }复制代码

  • 需要注意的是,如果某个线程来不及释放就异常中断了,会导致另一个线程一直在等,造成死锁。 虽然这个异常不在这个问题的考虑范围内 但是可以使用finally 来包裹释放锁资源
同步锁打印
  • 让两个线程使用同一把锁。交替执行 。
    • 判断是不是奇数 如果是奇数进入奇数线程执行打印并加一。然后线程释放锁资源。然后让该线程等待
    • 判断是不是偶数,如果是偶数进入偶数线程执行打印并加一。然后线程释放锁资源。然后让该线程等待
import java.util.concurrent.atomic.AtomicInteger; /** * @ClassName AlternatePrinting * @Author yunlogn * @Date 2019/5/21 * @Description 交替打印奇偶数 */ public class AlternatePrinting { public static AtomicInteger atomicInteger = new AtomicInteger(1); public static void main(String[] args) throws InterruptedException {Thread a=new Thread(new AThread()); Thread b=new Thread(new BThread()); a.start(); b.start(); } public static class AThread implements Runnable {@Override public void run() { while (true) { synchronized (atomicInteger) { if (atomicInteger.intValue() % 2 != 0) { System.out.println("奇数线程:" + atomicInteger.intValue()); atomicInteger.getAndIncrement(); // 奇数线程释放锁资源 atomicInteger.notify(); try { atomicInteger.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { try { // 奇数线程等待 atomicInteger.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } public static class BThread implements Runnable {@Override public void run() { while (true){ synchronized (atomicInteger){ if(atomicInteger.intValue() %2== 0 ){ System.out.println("偶数线程:"+ atomicInteger.intValue()); atomicInteger.getAndIncrement(); // 偶数线程释放锁资源 atomicInteger.notify(); try { atomicInteger.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else{ try { // 偶数线程等待 atomicInteger.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }}复制代码

    推荐阅读