开发者

Minimum time lock in java

I'm looking for a way to limit access to a method in java to no more than once every X seconds. Here's my situation :

I want to run this code in parallel with multiple threads :

private MyService service;
public void run() {
    // Send request to remote service
    InputStream开发者_运维问答 response = service.executeRequest();

    // Process response
    ... some more code
}

The executeRequest() method sends an http request to a remote server (which isn't mine, I have no access to its implementation) and waits for the response from the server. It then does some processing of the data. I would like to have many threads run this in parallel. My problem is that the remote server will crash if too many requests are sent simultaneously. So I want some way of making sure that the executeRequest() method will never be called more than once every second.

Do you know how I could do that in java ? Thanks


Hrm, I'm not sure that restricting the frequency of access to a method will result in prevention of overload.

Perhaps there isn't enough info in the above post, but it seems that a WorkerThread + JobQueue setup would work just fine here.

Food for thought: Multithreaded job queue manager

EDIT: Trying to be a bit less vague...

  • Have the server collect the requests into some data structure, perhaps a class called Job.
  • Have Jobs then be placed into the bottom of a Queue.
  • Have WorkerThread objects pop Job objects off the top of the Queue and process them.
  • Make sure to only instantiate as many WorkerThread objects as you need to maintain proper server load. Only experimentation will determine this number, but as a very rough rule, start with # of processing cores - 1. (aka start 7 workers on an 8 core machine)

EDIT #2 In light of new information:

  • Setup a Queue on the client side
  • Make a Worker that can track what Jobs have been submitted, which Jobs have gotten a response and which Jobs are still processing. This will allow for limiting the number of Jobs being submitted at any one time.
  • Make Worker track 'lastSubmissionTime' to prevent any submission occuring < 1 second from previous


You could use a Semaphore to throttle the number of threads able to call executeRequest():

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

The executing thread could increment the semaphore prior to entering the execute and other threads could wait for it to fall to either 0 or a number which reflects how many are allowed to run in parallel.

A Timertask:

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

Could be used to decrement the semaphore after 3 seconds... Throttling the entry to no more than 1 new entrant every 3 seconds:


Limiting concurrency on the client side is not a good pattern — how are clients supposed to know about each other?


Using dynamic proxy you can wrap your service and handle max execution in the InvocationHandler:

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

where naive implementation of InvocationHandler could look something like this:

  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);
    }
  }


In your controller class where you call worker to execute the service, use ExecutorService to start the pool of threads to ultilize the worker class.

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

To add to what others have already suggested, Have a worker class to take task from the queue execute and wait for the number of minutes as you require before taking another task from the queue. I have made this 2min as an example.

Example:

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);
    }
  }
}


Have you thought about using sleep to have the threads pause before jumping to hit the remote call? You could have the threads sleep for a random # of seconds between 1 and 5 which would limit the number of threads hitting the method at any one time.

You could also put a lock on the method that expires after 1 second so each thread "grabs" the lock, executes the method, but its lock expires so the next thread can grab it and execute. Put the lock at the beginning of the method -- and have it do nothing except hold the thread for one second thread.sleep(1000) and then continue execution. That will limit you to one thread hitting the method at a time.

EDIT: Response to OP's Comment Below

   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();
     }
   }
 }

Modified from: ReentrantLock. Inside the try/catch, all you do is thread.sleep(1000) instead of actually doing something. It then releases the lock for the next thread and continues executing the rest of the method body--in your case the connection to the remote server.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜