查看: 1364| 回复: 5
跳转到指定楼层
上一主题 下一主题
收起左侧

[其他] 多线性打印数字

全局:

注册一亩三分地论坛,查看更多干货!

您需要 登录 才可以下载或查看附件。没有帐号?注册账号

x
多线性的面试题目不在少数。可是题目似乎不多。找了找,发现这个题目:

需要用多线性来实现:
您好!
本帖隐藏的内容需要积分高于 200 才可浏览
您当前积分为 0。
使用VIP即刻解锁阅读权限或查看其他获取积分的方式
游客,您好!
本帖隐藏的内容需要积分高于 200 才可浏览
您当前积分为 0。
VIP即刻解锁阅读权限查看其他获取积分的方式
Unlock interview details and practice with AI
Curated Interview Questions from Top Companies



有什么更好的方法,或者局部修正,请不吝赐教。



上一篇:到什么级别之后面试不用刷题了?
下一篇:后续之[绝望+无奈,CS master 究竟该继续找工作还是回国]
🔗
magicsets 2019-5-31 13:00:41 | 只看该作者
全局:
每个工人通知下一个工人的想法是很好的,不过代码里把同步机制(各种lock、barrier,还有synchronized关键字)与真正的业务逻辑(也就是累加并打印数字)混合在一起了,看起来会比较乱

所以一个改进是如何把同步机制给解耦(decoupling)出来,这里常用的一种抽象是BlockingQueue:各个工人之间通过阻塞队列发送接收消息,而所有同步操作都可以封装在阻塞队列里面

  1. import java.util.ArrayList;
  2. import java.util.LinkedList;
  3. import java.util.Queue;
  4. import java.util.concurrent.locks.Condition;
  5. import java.util.concurrent.locks.Lock;
  6. import java.util.concurrent.locks.ReentrantLock;

  7. public class Main {
  8.   public static void main(String[] args) throws InterruptedException {
  9.     // 参数
  10.     final int K = 10;
  11.     final int N = 3;   
  12.     final int MAX_VALUE = K * N;

  13.     // 初始化N个消息管道,这份代码里手写了一个简易的阻塞消息管道
  14.     // 也可以用Java自带的LinkedBlockingDeque
  15.     ArrayList<SimpleBlockingQueue<Integer>> messageQueues = new ArrayList<>();
  16.     for (int i = 0; i < N; ++i) {
  17.       messageQueues.add(new SimpleBlockingQueue<Integer>());
  18.     }
  19.    
  20.     // 初始化N个worker,每一个worker对应一个线程
  21.     ArrayList<Thread> workerThreads = new ArrayList<>();
  22.     for (int i = 0; i < N; ++i) {
  23.       // 当前worker的outgoing消息队列即是下一个worker的incoming消息队列,构成一个环路
  24.       Worker worker = new Worker(i, MAX_VALUE,
  25.                                  messageQueues.get(i),
  26.                                  messageQueues.get(i == N - 1 ? 0 : i + 1));
  27.       Thread thread = new Thread(worker);
  28.       thread.start();
  29.       workerThreads.add(thread);
  30.     }

  31.     // 给0号worker发送启动消息
  32.     messageQueues.get(0).add(0);
  33.    
  34.     // 等待所有线程执行结束
  35.     for (Thread thread : workerThreads) {
  36.       thread.join();
  37.     }
  38.   }
  39. }

  40. class Worker implements Runnable {
  41.   private int workerId;
  42.   private int maxValueExclusive;
  43.   private SimpleBlockingQueue<Integer> incomingMessages;
  44.   private SimpleBlockingQueue<Integer> outgoingMessages;

  45.   public Worker(int workerId, int maxValueExclusive,
  46.                 SimpleBlockingQueue<Integer> incomingMessages,
  47.                 SimpleBlockingQueue<Integer> outgoingMessages) {
  48.     this.workerId = workerId;
  49.     this.maxValueExclusive = maxValueExclusive;
  50.     this.incomingMessages = incomingMessages;
  51.     this.outgoingMessages = outgoingMessages;
  52.   }
  53.   
  54.   @Override
  55.   public void run() {
  56.     while (true) {
  57.       Integer token = null;
  58.       // 从上一个worker那里获得token,当前持有token的worker才被允许打印数字
  59.       // 为了简洁,token本身就是一个Integer顺便记录了需要打印的数字
  60.       try {
  61.         token = incomingMessages.take();
  62.       } catch (InterruptedException e) {}
  63.       // 出错或者是超过最大值的情况
  64.       if (token == null || token >= maxValueExclusive) {
  65.         // 仍然需要传递token来通知下游worker终止运行
  66.         outgoingMessages.add(maxValueExclusive);
  67.         break;
  68.       }
  69.       System.out.println("Thread " + workerId + " is now printing " + token);
  70.       outgoingMessages.add(token + 1);      
  71.     }
  72.   }
  73. }

  74. // 简易的BlockingQueue实现
  75. class SimpleBlockingQueue<E> {
  76.   private Lock lock = new ReentrantLock();
  77.   private Condition isEmptyCond = lock.newCondition();
  78.   private Queue<E> items = new LinkedList<>();
  79.   
  80.   public void add(E item) {
  81.     lock.lock();
  82.     items.add(item);
  83.     if (items.size() == 1) {
  84.       isEmptyCond.signalAll();
  85.     }
  86.     lock.unlock();
  87.   }
  88.   
  89.   public E take() throws InterruptedException {
  90.     lock.lock();
  91.     try {
  92.       while (items.isEmpty()) {
  93.         isEmptyCond.await();
  94.       }
  95.       return items.poll();
  96.     } finally {
  97.       lock.unlock();
  98.     }
  99.   }
  100. }
复制代码

评分

参与人数 1大米 +10 收起 理由
14417335 + 10

查看全部评分

回复

使用道具 举报

🔗
magicsets 2019-5-31 13:15:34 | 只看该作者
全局:
另一种更具一般性的实现方式是引入一个协调(调度)线程,也就是常说的coordinator

然后worker和coordinator之间通过消息通信,各自有一个event loop根据消息类型执行对应的操作 —— 这种并发模型称作actor model:https://en.wikipedia.org/wiki/Actor_model

  1. import java.util.ArrayList;
  2. import java.util.concurrent.BlockingQueue;
  3. import java.util.concurrent.LinkedBlockingQueue;

  4. public class Main {
  5.   public static void main(String[] args) throws InterruptedException {
  6.     // 参数
  7.     final int K = 10;
  8.     final int N = 3;   
  9.     final int MAX_VALUE = K * N;

  10.     // 创建并启动coordinator线程
  11.     Thread coordinatorThread = new Thread(new Coordinator(N, MAX_VALUE));
  12.     coordinatorThread.start();
  13.     coordinatorThread.join();
  14.   }
  15. }

  16. class Coordinator implements Runnable {
  17.   private ArrayList<Worker> workers = new ArrayList<>();
  18.   private ArrayList<Thread> workerThreads = new ArrayList<>();
  19.   private BlockingQueue<Message> incomingMessages = new LinkedBlockingQueue<>();
  20.   private int maxValue;
  21.   
  22.   public Coordinator(int numWorkers, int maxValue) {
  23.     // Coordinator负责创建并管理所有worker
  24.     for (int i = 0; i < numWorkers; ++i) {
  25.       Worker worker = new Worker(i, this);
  26.       workers.add(worker);
  27.       workerThreads.add(new Thread(worker));
  28.     }
  29.     this.maxValue = maxValue;
  30.   }

  31.   public void sendMessage(Message message) {
  32.     incomingMessages.add(message);
  33.   }

  34.   @Override
  35.   public void run() {
  36.     // 启动所有worker线程
  37.     for (Thread thread : workerThreads) {
  38.       thread.start();
  39.     }
  40.     // 依次向各个worker发送打印数字的消息
  41.     for (int i = 0; i < maxValue; ++i) {
  42.       Worker worker = workers.get(i % workerThreads.size());
  43.       worker.sendMessage(new PrintValueMessage(i));
  44.       // 等待收到worker回应后才开始下一个循环
  45.       try {
  46.         incomingMessages.take();
  47.       } catch (InterruptedException e) {}
  48.     }
  49.     // 完成所有打印后,向各个worker发送终止消息
  50.     for (Worker worker : workers) {
  51.       worker.sendMessage(new JoinMessage());
  52.     }
  53.     // 等待worker线程终止
  54.     for (Thread thread : workerThreads) {
  55.       try {
  56.         thread.join();
  57.       } catch (InterruptedException e) {}
  58.     }   
  59.   }
  60. }

  61. class Worker implements Runnable {
  62.   private int workerId;
  63.   private BlockingQueue<Message> incomingMessages =
  64.       new LinkedBlockingQueue<>();
  65.   private Coordinator coordinator;

  66.   public Worker(int workerId, Coordinator coordinator) {
  67.     this.workerId = workerId;
  68.     this.coordinator = coordinator;
  69.   }
  70.   
  71.   public void sendMessage(Message message) {
  72.     incomingMessages.add(message);
  73.   }
  74.    
  75.   @Override
  76.   public void run() {
  77.     boolean running = true;
  78.     while (running) {
  79.       // 等待coordinator发来消息
  80.       Message msg = null;
  81.       try {
  82.         msg = incomingMessages.take();
  83.       } catch (InterruptedException e) {
  84.         continue;
  85.       }
  86.       // 消息有两种:打印数字或者是终止运行,根据消息类型执行对应操作
  87.       switch (msg.getKind()) {
  88.         case PRINT_VALUE:
  89.           System.out.println("Thread " + workerId + " is now printing " +
  90.                              ((PrintValueMessage)msg).getValue());
  91.           coordinator.sendMessage(new AckMessage());
  92.           break;
  93.         case JOIN:
  94.           running = false;
  95.           break;
  96.         default:
  97.           System.err.println("Unexpected message received");
  98.       }
  99.     }
  100.   }
  101. }

  102. // 这里添加了三种消息类型,还可以扩展添加各种各样的消息
  103. enum MessageKind {
  104.   PRINT_VALUE,
  105.   JOIN,
  106.   ACK
  107. }

  108. abstract class Message {
  109.   private MessageKind kind;

  110.   public Message(MessageKind kind) {
  111.     this.kind = kind;
  112.   }
  113.   public MessageKind getKind() {
  114.     return kind;
  115.   }  
  116. }

  117. class PrintValueMessage extends Message {
  118.   private final int value;

  119.   public PrintValueMessage(int value) {
  120.     super(MessageKind.PRINT_VALUE);
  121.     this.value = value;
  122.   }  
  123.   public int getValue() {
  124.     return value;
  125.   }  
  126. }

  127. class JoinMessage extends Message {
  128.   public JoinMessage() {
  129.     super(MessageKind.JOIN);
  130.   }
  131. }

  132. class AckMessage extends Message {
  133.   public AckMessage() {
  134.     super(MessageKind.ACK);
  135.   }
  136. }
复制代码

评分

参与人数 1大米 +15 收起 理由
14417335 + 15

查看全部评分

回复

使用道具 举报

🔗
yuqinlear 2019-6-2 01:07:38 | 只看该作者
全局:
问下你怎么保证line48能获取到了锁?
你提到notifyall这个效率问题,用condition来隔离呼唤单个线程应该就行了吧。
回复

使用道具 举报

🔗
 楼主| 14417335 2019-6-2 02:21:00 | 只看该作者
全局:
yuqinlear 发表于 2019-6-1 12:07
问下你怎么保证line48能获取到了锁?
你提到notifyall这个效率问题,用condition来隔离呼唤单个线程应该 ...

在每个进程的一开始都有synchronized (myLock),然后barrier保证了每个工人都拿到了锁。如果48行的锁owner工人没有放弃monitor,那么本工人是无法在48行拿到锁的,会一直在那里等待owner工人进入wait。

对。condition也很方便。基本上是一样的操作。需要await和lock,unlock配合使用。另外每个线程需要一个条件来判断目前该await还是被notify了。

回复

使用道具 举报

🔗
 楼主| 14417335 2019-7-29 02:39:04 | 只看该作者
全局:
刚刚发现leetcode现在也有了多线性的问题了。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表