Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <blockquote> <p>If the Http connection fails (e.g. server is down), I need to decrement the counter.</p> </blockquote> <p>I was going to say "hell yes", but I'm somewhat less certain after this sentence. I take it you want to do something like this:</p> <pre> def sendRequest(url) request = new request to url request.header["X-count"] = next serial if request.send() != SUCCESS rewind serial </pre> <p>In that case, I'd guess that two threads should not be allowed to send requests simultaneously, and then you want something that serializes requests rather than an <code>AtomicInteger</code>, which really just lets you perform a few operations atomically. If two threads were to call <code>sendRequest</code> simultaneously, and the first one would fail, this would happen:</p> <pre> Thread | What happens? --------+------------------------- A | Creates new request B | Creates new request A | Set request["X-count"] = 0 A | Increment counter to 1 A | Send request B | Set request["X-count"] = 1 B | Increment counter to 2 B | Send request A | Request fails B | Request succeeds A | Rewind counter down to 1 C | Creates new request C | Set request["X-count"] = 1 C | Increment counter to 2 </pre> <p>And now, you've sent two request with X-count = 1. If you want to avoid this, you should use something like (assume <code>Request</code> and <code>Response</code> are classes used to handle requests to URLs):</p> <pre><code>class SerialRequester { private volatile int currentSerial = 0; public synchronized Response sendRequest(URL url) throws SomeException { Request request = new Request(url); request.setHeader("X-count", currentSerial); Response response = request.send(); if (response.isSuccess()) ++currentSerial; return response; } } </code></pre> <p>This class guarantees that no two successful requests (made through the same <code>SerialRequester</code>) have the same X-count value.</p> <p><strong>Edit</strong> Many seem concerned about the above solution not running concurrently. It doesn't. That's correct. But it needs to work this way to solve the OP's problem. Now, if the counter needn't be decremented when a request fails, an <code>AtomicInteger</code> would be perfect, but it's incorrect in this scenario.</p> <p><strong>Edit 2</strong> I got it in me to write a serial requester (like the one above) less prone to freezing, such that it aborts requests if they've been pending too long (i.e., queued in the worker thread but not started). Thus, if the pipes clog and one request hangs for a very very long time, other requests will wait at most a fixed amount of time, so the queue doesn't grow indefinitely until the clog goes away.</p> <pre><code>class SerialRequester { private enum State { PENDING, STARTED, ABORTED } private final ExecutorService executor = Executors.newSingleThreadExecutor(); private int currentSerial = 0; // not volatile, used from executor thread only public Response sendRequest(final URL url) throws SomeException, InterruptedException { final AtomicReference&lt;State&gt; state = new AtomicReference&lt;State&gt;(State.PENDING); Future&lt;Response&gt; result = executor.submit(new Callable&lt;Response&gt;(){ @Override public Result call() throws SomeException { if (!state.compareAndSet(State.PENDING, State.STARTED)) return null; // Aborted by calling thread Request request = new Request(url); request.setHeader("X-count", currentSerial); Response response = request.send(); if (response.isSuccess()) ++currentSerial; return response; } }); try { try { // Wait at most 30 secs for request to start return result.get(30, TimeUnit.SECONDS); } catch (TimeoutException e){ // 30 secs passed; abort task if not started if (state.compareAndSet(State.PENDING, State.ABORTED)) throw new SomeException("Request queued too long", e); return result.get(); // Task started; wait for completion } } catch (ExecutionException e) { // Network timeout, misc I/O errors etc throw new SomeException("Request error", e); } } } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload