我正在寻找一种限制访问java方法的方法,每X秒不超过一次。这是我的情况:
我想与多个线程并行运行此代码:
private MyService service;
public void run() {
// Send request to remote service
InputStream response = service.executeRequest();
// Process response
... some more code
}
executeRequest()方法将http请求发送到远程服务器(不是我的,我无法访问其实现)并等待来自服务器的响应。然后它会对数据进行一些处理。 我想有很多线程同时运行它。我的问题是如果同时发送太多请求,远程服务器将崩溃。所以我想要一些方法来确保executeRequest()方法永远不会每秒被调用一次。
你知道我怎么能在java中做到这一点吗?感谢
答案 0 :(得分:4)
嗯,我不确定限制访问方法的频率会导致防止过载。
也许在上面的帖子中没有足够的信息,但似乎WorkerThread + JobQueue设置在这里工作得很好。
思考的食物: Multithreaded job queue manager
编辑:试着变得不那么模糊......
编辑#2 根据新信息:
答案 1 :(得分:2)
您可以使用信号量来限制能够调用executeRequest()的线程数:
http://download.oracle.com/javase/1,5,0/docs/api/java/util/concurrent/Semaphore.html
执行线程可以在进入执行之前递增信号量,而其他线程可以等待它降为0或反映允许并行运行的数量的数字。
时间任务:
http://download.oracle.com/javase/1.4.2/docs/api/java/util/TimerTask.html
可用于在3秒后递减信号量...每3秒将条目限制为不超过1个新进入者:
答案 2 :(得分:1)
限制客户端的并发性并不是一个好的模式 - 客户应该如何相互了解?
答案 3 :(得分: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)而不是实际做某事。然后它释放下一个线程的锁并继续执行方法体的其余部分 - 在您的情况下是与远程服务器的连接。
答案 4 :(得分: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);
}
}
答案 5 :(得分:0)
在您调用worker来执行服务的控制器类中,使用ExecutorService启动线程池以使用该类。
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(new MyServiceImpl(someObject));
要添加其他人已经建议的内容,请让一个worker类从队列执行任务并在从队列中执行另一个任务之前等待所需的分钟数。我把这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);
}
}
}