2011-09-30 58 views
2

我正在寻找一种方法来限制对java中的方法的访问,不超过每X秒一次。这里是我的情况:在java中的最短时间锁定

我想在多线程并行运行这段代码:

private MyService service; 
public void run() { 
    // Send request to remote service 
    InputStream response = service.executeRequest(); 

    // Process response 
    ... some more code 
} 

的executeRequest()方法将HTTP请求发送到远程服务器(这是不是我的,我没有访问其实现)并等待来自服务器的响应。然后它会对数据进行一些处理。 我想有很多线程并行运行它。我的问题是,如果同时发送太多的请求,远程服务器将崩溃。所以我想要一些确保executeRequest()方法永远不会每秒调用一次的方法。

你知道我怎么能在java中做到这一点?谢谢

+1

什么限制并发执行的数量和阻断新的呼叫,直到服务实例可用?您是否使用任何特定的技术为您的远程服务器? – Thomas

+0

远程服务器不是我的。 MyService.executeRequest向远程服务器发送http请求并等待响应。我无法控制它。 – jonasr

回答

2

<1秒你可以使用一个信号量节流线程能够调用executeRequest()的数量:

http://download.oracle.com/javase/1,5,0/docs/api/java/util/concurrent/Semaphore.html

正在执行的线程可能会在进入execute之前增加信号量,其他线程可能会等待它降至0或数字,这反映了允许并行运行的数量。

一个TimerTask:

http://download.oracle.com/javase/1.4.2/docs/api/java/util/TimerTask.html

可以用了3秒后减小信号...进入节流不超过1新进入者每3秒:

+0

信号量+1。忘了那些。 – FloppyDisk

+0

这对我有效,谢谢 – jonasr

4

Hrm,我不确定限制访问方法的频率将导致防止过载。

也许在上面的帖子中没有足够的信息,但似乎WorkerThread + JobQueue设置在这里工作得很好。

深思: Multithreaded job queue manager

编辑:试图少一点含糊......

  • 已在服务器收集请求到一些数据结构, 也许是一类被称为工作。
  • 有工作然后被放置在队列的底部 。
  • 让WorkerThread对象将Job对象从队列顶部的 上弹出并处理它们。
  • 确保只需要实例化多个WorkerThread对象,因为您需要维护适当的服务器负载。 只有实验将确定该数量,但作为一个非常粗糙 规则,开始处理核# - 1(又名开始7名工人 一个8芯机)

EDIT#2在光新的信息:在客户端

  • 做一个工人,可以追踪乔布斯已经提交什么

    • 设置一个队列,其工作变得响应和作业仍在处理。这将允许限制任何时候提交的作业数量。
    • 制作工人的轨道“lastSubmissionTime”,以防止任何提交存在的从以前
  • +0

    对不起,我不够精确。远程服务器不是我的。我正在向远程服务器发送http请求并等待响应。我不知道它是如何构建的,但是在太短的时间间隔内发送太多的请求会使其崩溃,这就是为什么我想限制每秒发送一次的请求的数量 – jonasr

    +0

    啊,在这种情况下,构建在客户端的Job和JobQueue,并且有一个工作人员管理向服务器提交的作业。您可以设置它,以便工人每秒提交一份工作,或者您可以通过跟踪提交和回复来跟踪工作中有多少未完成的工作......或者同时执行两项操作:) – claymore1977

    0

    你有没有想过关于使用sleep在跳转到远程呼叫之前让线程暂停?您可以让线程在1到5之间随机选择一个秒,这将限制任何时候触发该方法的线程数。

    你也可以在1秒后过期的方法上放一个lock,这样每个线程都会“抓取”锁,执行该方法,但其锁会失效,以便下一个线程可以抓住它并执行。把锁放在方法的开始处 - 除非保持线程一秒thread.sleep(1000)然后继续执行。这将限制您一次只能触发该方法的一个线程。

    编辑:应对OP的评论下面

    class X { 
        private final ReentrantLock lock = new ReentrantLock(); 
        // ... 
    
        public void m() { 
        lock.lock(); // block until condition holds 
        try { 
         thread.sleep(1000) //added to example by floppydisk. 
        } finally { 
         lock.unlock() 
         doYourConnectionHere(); 
        } 
        } 
    } 
    

    改自:ReentrantLock。在try/catch内部,你所做的只是thread.sleep(1000)而不是实际做某件事。然后它释放下一个线程的锁并继续执行方法体的其余部分 - 在你的情况下连接到远程服务器。

    +0

    你的第一个建议对我来说并不真正有效,因为没有什么能够阻止两个线程同时唤醒并几乎同时调用服务。你的第二个建议是我正在寻找的东西,但我没有找到它的一个例子。 – jonasr

    +0

    @jonasr:更新了答案以提供更好的代码示例。 – FloppyDisk

    +0

    这个问题是每个线程在调用方法之前都会等待1秒,即使他们不需要。这可能不是很多,但如果我需要延长它可能是一个问题 – jonasr

    0

    使用动态代理你可以用你的服务,并在InvocationHandler的处理最高执行:

    MyService proxy = (MyService) Proxy.newProxyInstance(// 
        MyService.class.getClassLoader(), // 
        new Class[] {MyService.class}, // 
        new MaxInvocationHandler()); 
    

    哪里天真实现的InvocationHandler的可能是这个样子:

    class MaxInvocationHandler implements InvocationHandler { 
        private static final long MAX_INTERVAL = 1000L; 
        private static final long MAX_INVOCATIONS = 1; 
    
        AtomicLong time = new AtomicLong(); 
        AtomicLong counter = new AtomicLong(); 
    
        @Override 
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
         long currentTime = System.currentTimeMillis(); 
         if (time.get() < currentTime) { 
         time.set(currentTime + MAX_INTERVAL); 
         counter.set(1); 
         } else if(counter.incrementAndGet() > MAX_INVOCATIONS) { 
         throw new RuntimeException("Max invocation exceeded"); 
         } 
    
         return method.invoke(proxy, args); 
        } 
        } 
    
    1

    限制并发客户端上方不是一个好的模式—客户应该如何相互了解?

    0

    在您的控制器类中,您调用worker来执行该服务,请使用ExecutorService启动线程池以便使用该工作器类。

    ExecutorService pool = Executors.newFixedThreadPool(10); 
    pool.submit(new MyServiceImpl(someObject)); 
    

    要添加什么别人已经提出,有职工班采取任务从执行队列中,等待的分钟数,你走了另一任务队列之前需要。我以这2分钟为例。

    例子:

    public class MyServiceImpl implements MyService , Runnable { 
    
        public static final int MAX_SIZE = 10; 
        private final BlockingQueue<Object> queue = new ArrayBlockingQueue<Object>(MAX_SIZE); 
    
        @Override 
        public void run() { 
        try 
        { 
        Object obj; 
         while ((obj==queue.take()) != null) 
         { 
         executeRequest(obj); 
         //wait for 2 min 
         Thread.sleep(1000 * 60 * 2); 
         } 
        } 
        catch (InterruptedException e) 
        {} 
        } 
    
        public void executeRequest(Object obj) 
        { 
        // do routine 
        } 
    
        public MyServiceImpl (Object token) 
        { 
        try 
        { 
         queue.put(token); 
        } 
        catch (InterruptedException e) 
        { 
         throw new AssertionError(e); 
        } 
        } 
    }