多线程之切换输出

这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

一、多线程简介

  • 基本的 概念 是同时对多个任务加以控制,就是同时执行多个任务,通过提高资源使用效率来提高系统的效率。
  • 多线程 目的 :最大限度的利用CPU资源。
  • 多线程 优点 :1.资源利用率更好。 2. 程序设计在某些情况下更简单。 3.程序响应更快。
  • 线程执行过程(图源于网络)

1353351-20180425201208004-1326053784.png

二、实例解决方案解析(交替打印FooBar

Question: 提供一个类如下,两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。请设计修改程序,以确保 "foobar" 被输出 n 次。

class FooBar {
  public void foo() {
    for (int i = 0; i < n; i++) {
      print("foo");
    }
  }

  public void bar() {
    for (int i = 0; i < n; i++) {
      print("bar");
    }
  }
}
复制代码

1、使用 BlockingQueue

  • BlockingQueue (阻塞队列) : 一个支持两个附加操作的队列。一个线程生产对象,而另外一个线程消费这些对象。
  • 1、在队列为空时,获取元素的线程会等待队列变为非空。
  • 2、当队列满时,存储元素的线程会等待队列可用。
  • 应用场景:阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。一个线程将会持续生产新对象并将其插入到队列之中,直到队列达到它所能容纳的临界点。也就是说,它是有限的。如果该阻塞队列到达了其临界点,负责生产的线程将会在往里边插入新对象时发生阻塞。它会一直处于阻塞之中,直到负责消费的线程从队列中拿走一个对象。
  • 题解以及注释
public class FooBar {
    private int n;
    private BlockingQueue<Integer> bar = new LinkedBlockingQueue<>(1); // 最大容量为 1
    private BlockingQueue<Integer> foo = new LinkedBlockingQueue<>(1);
    public FooBar(int n) {
        this.n = n;
    }
    public void foo(Runnable printFoo) throws InterruptedException { // 可看作生产者
        for (int i = 0; i < n; i++) {
            foo.put(i);    // 执行一次后,就达到了容量最大值,需要等待消费者消费。
            printFoo.run();
            bar.put(i);
        }
    }

    public void bar(Runnable printBar) throws InterruptedException { // 可看作消费者
        for (int i = 0; i < n; i++) {
            bar.take();  // 必须在生产者生产后才能消费,消费完之后,又需要等待生产者下一次生产
            printBar.run();
            foo.take();
        }
    }
}
复制代码

2、使用 Thread.yield() (自旋 + 让出CPU)

  • Thread.yield() 方法作用:暂停当前正在执行的线程对象,并执行其他线程(可以这么理解)。而 Thread.yield() 真正的作用是让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会,如下图(源于网络)。

23214.png

  • 题解以及注释
class FooBar {
    private int n;

    public FooBar5(int n) {
        this.n = n;
    }

    volatile boolean permitFoo = true;  // 设置标识
    volatile boolean permitBar = False;

    public void foo(Runnable printFoo) throws InterruptedException {     
        for (int i = 0; i < n; ) {
            if (permitFoo) {
        	printFoo.run();
            	i++;
            	permitFoo = false;   // 转换状态
                permitBar = true;
            } else {
                Thread.yield();     // 暂停当前线程,执行其他线程
            }
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {       
        for (int i = 0; i < n; ) {
            if (permitBar) {
        	printBar.run();
        	i++;
        	permitFoo = true;    // 转换状态
                permitBar = false;
            } else {
                Thread.yield();     // 暂停当前线程,执行其他线程
            }
        }
    }
}
复制代码

三、参考:

猜你喜欢

转载自juejin.im/post/7035907799930372133