【问题标题】:Picky host (lock?)挑剔的主人(锁?)
【发布时间】:2013-12-25 17:19:30
【问题描述】:

我相信无论使用哪种语言都可以考虑我的问题,但是为了有一些“锚”,我将使用 Java 语言来描述它。

让我们考虑以下场景: 我有一个扩展 Thread 的类 PickyHost 和它的一个实例,pickyHostInst 正在运行。 该类可能如下所示:

class PickyHost extends Thread {
    private ArrayList<Guest> guests;
    public void enter(Guest g) {
        // deal with g
    }
    private void pickGuests() {
        // ...
    }
    public void run() {
        // listen indefinitely
    }
}

此外,在后台,我有许多 Guest 实例正在运行(它们还扩展了 Thread 类),并且偶尔会有一些 guest > 想要在 pickyHostInst 上调用 enter 方法,参数 g 是它本身。 现在,我希望 PickyHost 在以下意义上是挑剔的:

在有人调用 enter 方法后,它会将 g 放在 guests 列表的末尾并强制 g等待通知。此外(我认为这是问题的症结所在)它会自行休眠 5 秒,并以某种方式允许(在这 5 秒内)其他客人调用 enter 方法(如果发生这种情况,那么它会忘记关于它必须睡眠多长时间并将其闹钟重新设置为恰好再次睡眠 5 秒)- 我将其称为敏感睡眠

如您所见,如果有很多客人到达,pickyHostInst 的总睡眠时间可能会很长——例如:A 到达,然后 4 秒后 B 到达,然后再过 4 秒 C 到达,然后很快。但是,假设已经创建了一条客人链A,B,...,G,并且从G到达的那一刻到5秒后,没有人到达。 然后我希望 pickyHostInst 调用 pickGuests 方法,该方法使用某种算法确定 {A, B, ..., G 的 subset S } 的客人通知他们可以停止等待并继续做他们通常做的事情,并且从 guests 列表中删除 S 的元素。方法 pickGuests 可能需要一些时间才能完成,同时一些客人 H 可能已经到达并调用了 enter - 然后 enter 应该正常进行但 pickGuests 应该忽略 H 并在其最后一次调用结束时处理 {A, B, ..., G} - 而不是 {A, B, ..., G, H}。 完成 pickGuests 后,pickyHostInst 应该(这里我有 2 个想法 - 实施其中任何一个都会让我开心 :)) 要么

  1. 再次进入 5 秒的敏感睡眠,之后,如果 H 到达后没有客人,则再次调用 pickGuests,或者
  2. 像往常一样通过 enter 方法同时为客人提供服务,但仅在之后调用 pickGuests max("S 中的最后一位客人(从最后一次调用开始)通知 pickyHostInst 的时刻(例如:S 中的最后一个“谢谢你,主人先生”)", "在 5 秒后最后(最新)客人调用进入“)。

最后,经过长时间的介绍,我的问题 - 我需要哪些工具来完成这样的任务?不幸的是,我有点迷失在各种锁和多线程/锁定机制的丰富性中,无法辨别哪一个适合我的问题(或者哪些以某种方式组合)。

我将非常感谢一些能让我走上正轨的代码草图。

【问题讨论】:

  • tl;dr 和 all 这个 bold 无处不在 使 难以 制作感觉
  • 我会尝试只使用睡眠、等待、通知、通知所有。我怀疑在这项任务中我会遇到一些我无法通过使用它们来实现的东西。你有没有遇到什么特别的卡住的地方?
  • @MikeW - 我认为区分生活在程序中的实体可以帮助理解我相当长的信息。可能是错的...

标签: java multithreading wait synchronized notify


【解决方案1】:

您可以使用java.util.Timer 对象,该对象可以在enter 方法中重置。定时器任务将在它自己的线程中运行,如果它没有事先取消,它会为你挑选。

请注意,enter 方法将在多个 Guest 线程之一上运行。这意味着它可能应该同步。最简单的方法是将synchronized 关键字添加到Java 中的方法声明:public synchronized void enter(Guest g)。这将确保一次只有一位客人可以进入。你可以把定时器取消/重启代码放在这里。

java.util.Timer 的工作方式是通过抽象的java.util.TimerTask 类。这是Runnable 的一种类型,它也具有取消任务的方法。我的建议是安排一个任务,当客人进入时,每隔 5000 毫秒就会挑选客人。如果前一个客人的任务正在运行,请先取消它。

enter 方法应该获取客户的锁(使用同步块)并让客户等待。拣货应在您选择的客人上调用notify() 方法。这将允许它们继续执行。

当您从队列中删除选定的客人时,请注意 Java 集合在默认情况下不是线程安全的。您必须使用外部锁来确保在您添加和删除客人时没有其他人正在修改您的列表。 Collections.synchronizedList(List) 方法提供了一种方便的方法来执行此操作。

以下是讨论我提到的主题的链接列表:

  1. http://docs.oracle.com/javase/tutorial/essential/concurrency/(初学者的优秀教程)
  2. http://docs.oracle.com/javase/7/docs/api/java/util/Timer.html
  3. http://docs.oracle.com/javase/7/docs/api/java/util/TimerTask.html
  4. http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#synchronizedList%28java.util.List%29

【讨论】:

    【解决方案2】:

    我可能会这样做。我会尽量避免使用notify/notifyAll,因为由于虚假唤醒,您将不得不涉及一个标志,这会使代码变得相当混乱。 CountDownLatch 在 IMO 是一个更好的选择,尽管这个名字有点奇怪。

    static final long five_sec = TimeUnit.SECOND.toNanos(5)
    final Queue<Pair<Guest, CountDownLatch>> guests = new LinkedList<>();
    long earliest = -1;
    
    // Synchronizing on "this" is fine but using private lock 
    // object is even better
    final Object lock = new Object();
    
    
    void enter(Guest g){
        Pair p = Pair.of(g, new CountDownLatch(1));
        synchronized(lock){
            guests.get().add(p);
            earliest = System.nanoTime() + five_sec;
        }
        p.second.await();
    }
    
    void pickGuests(){
        synchronized(lock){
            // pop a few guests from sofar and wake them
            Guest g = sofar.poll();
            if(g != null){
                g.second.countDown();
            }
        }
    
    }
    
    void run(){
        while(!Thread.currentThread().isInterrupted()){
            long sleepTime;
            synchronized(lock){
                if(System.nanoTime() > earliest){
                    pickGuests();
                }
                sleepTime = earliest - System.nanoTime();
                sleepTime = sleepTime < five_sec ? five_sec : sleepTime;
            }
            Thread.sleep(sleepTime);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多