【问题标题】:Implement a monitor thread in Java用Java实现一个监控线程
【发布时间】:2012-02-14 22:37:56
【问题描述】:

嗯,我不熟悉java中的线程,所以我正在处理这个问题:我有一个包含一些对象(比如说会话)的单例对象,每个对象都有一个持续时间,所以这意味着经过一些当一个对象被认为已过期,因此需要从(一个池 - 单例中的列表)单例中删除它。为此,我决定有一个线程每 5 分钟(或 10 分钟或其他)检查一次,并清理单例类中的所有会话。我怎样才能实现这样的功能,避免任何可能的死锁和/或耗时的块。先感谢您。

【问题讨论】:

    标签: java multithreading deadlock monitor


    【解决方案1】:

    我同意@quaylar (+1),如果可以的话,请使用现有的缓存技术。

    但是,如果您不能,一种解决方案是使用 java.util.Timer。用直到第一个会话对象到期的时间初始化它并让它进入睡眠状态。然后,在它唤醒时,让它删除您的会话对象并将其重置为下一个到期时间。让java处理时间方面。

    【讨论】:

    • 好吧,问题是每次调用会话时会话时间都会更新,如果会话在一段时间内(例如预定义的时间)处于非活动状态,它会保持不变。所以我不知道 Timer 是否可更新(重置时间)?这是否意味着我们应该为每个会话设置一个计时器?如果是这样,那么内存消耗会很大。
    • @elvis 嗨,整个缓存只需要一个计时器。这种方法总是将计时器设置为所需的偏移量,直到下一个对象过期。因此,您不会在每次唤醒时将计时器设置为 30 秒,而是始终将其设置为下一次到期之前的任何时间......现在,除此之外取决于您的底层缓存数据结构。如果它是有序的(TreeMap 或 List),那么您知道按顺序排列的下一个对象是下一个过期对象。因此,无需迭代列表来查找下一个将过期的项目。
    • 你可以使用任何定时器机制,重要的是方法,而不是实现,只要虚拟机在看你的定时器(守护进程)。
    • 拥有一个有序的集合可以让您只检查尾部或头部,因此它更加清晰和快速。问题是:插入速度较慢,检索速度较慢,结构实际上是一个映射,因为我需要通过它的 id 快速找到一个会话,而不必遍历整个集合。还有另一个问题,每次更新会话时,都必须将其删除并重新进入已排序的集合,因为该集合将是未排序的(或者我错了?),如果是这样,那么这会增加复杂性。
    • @Elvis:首先很抱歉,但当我指的是 LinkedHashMap 时,我写了 TreeMap。回到正轨,LinkedHashMap 是一个有序集合,允许通过 key/id 检索(实现 Map 接口,如 HashMap),但保持插入顺序。与迭代列表以删除要删除的项目相比,插入会更慢但边缘化。关于“每次更新会话时,必须将其删除并重新输入到已排序的集合中,因为集合”,我原以为您会在会话过期时从缓存中删除会话并重新添加它如果/更新时。
    【解决方案2】:

    你可以这样做:我假设你的单例对象的类被称为单例所以你有这样的东西(它不是完美的代码)

    public class Singleton {
       List<Objects> singletonList = Collections.synchronizedList(new ArrayList<Objects>);
    
    
    
    }
    
    public class RemoveExpiredItemsThread implements Runnable {
    
        private Singleton singletonReference;
        private int sleepTime = 5*60*1000;
        // the constructor
    
        // then the run method which is something like this
    
        public void run() {
           while(done == false) {
             Thread.sleep(sleepTime);
             singletonReference.removeItem();
           }  
        }
    
    }
    

    【讨论】:

    • 实际上这正是我所做的,但我不能确定这是否是正确的解决方案。
    【解决方案3】:

    我不会那样实现它。相反,当一个会话被请求到池中时,我会删除超时的会话(尽管每次获取都不需要)。顺便说一句,这是 Guava's CacheBuilder 所做的,您可以使用它,因为它简单、经过测试并提供有用的功能。

    如果您真的想这样做,那么您可能应该使用 ConcurrentMap 或 ConcurrentList,并使用 single-thread ScheduledExecutorService,这将唤醒迭代列表并每 X 分钟删除旧会话。

    【讨论】:

    • 嗯,这是我一开始的想法,但问题是:如果我们打开了 100 万个会话,其中 1000 个在没有调用删除会话方法的情况下断开连接,该怎么办。所以这会将会话保留在内存中。
    • 当从列表中获取一个会话时,缓存可以遍历会话并删除过时的会话。从缓存中获取会话应该非常频繁,否则缓存不会很有用。
    • 使用弱引用创建会话对象,以便它们首先被垃圾收集。
    【解决方案4】:
    Runnable cleaner = new Runnable() {
    public void run() { /* remove expired objects here */ 
         //Using get method check whether object is expired 
       }
    };
    Executors.newScheduledThreadPool(1)
           .scheduleWithFixedDelay(cleaner, 0, 30, TimeUnit.SECONDS);
    

    【讨论】:

    • 这可能就是我需要的。我不知道这个类(Executor 及其所有派生类)谢谢您的回复。
    【解决方案5】:

    您是否可以选择使用预先存在的内存缓存解决方案而不是自己编写? 如果是,您可以查看 Google Guava,它提供了许多其他的缓存解决方案。

    1. 见:Caching with Guava
    2. 见:http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/cache/CacheBuilder.html

    【讨论】:

    • 可能是这样,但我暂时需要一个简单的解决方案,而无需学习新的 API。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-18
    • 2021-05-11
    • 2010-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多