我有一个Android客户端,它将与服务器建立Http连接。
服务器要求所有Http请求在Http头中提供单调递增的计数器。 e.g。
POST /foo/server
X-count: 43
将启动Http连接的地方:
Context#startService
启动)为了显示计数器值,我计划在AtomicInteger
子类中托管Application
。然后,所有代码将从中心位置检索计数。
如果Http连接失败(例如服务器关闭),我需要递减计数器。
你认为AtomicInteger非常适合我的场景吗?
答案 0 :(得分:12)
AtomicInteger正是您想要用于此目的的。
答案 1 :(得分:2)
如果Http连接失败(例如服务器关闭),我需要递减计数器。
我打算说“地狱好”,但在这句话后我不太确定。我认为你想做这样的事情:
def sendRequest(url) request = new request to url request.header["X-count"] = next serial if request.send() != SUCCESS rewind serial
在这种情况下,我猜不应该允许两个线程同时发送请求,然后你想要一些序列化请求而不是AtomicInteger
的东西,它实际上只是让你以原子方式执行一些操作。如果两个线程同时调用sendRequest
,并且第一个线程将失败,则会发生这种情况:
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
现在,你发送了两个X-count = 1的请求。如果你想避免这种情况,你应该使用类似的东西(假设Request
和Response
是用来处理请求的类到URL):
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;
}
}
此类保证没有两个成功的请求(通过相同的SerialRequester
生成)具有相同的X计数值。
编辑许多人似乎都担心上述解决方案无法并发运行。它没有。那是对的。但它需要以这种方式解决OP的问题。现在,如果在请求失败时不需要递减计数器,AtomicInteger
将是完美的,但在这种情况下它是不正确的。
编辑2 我得到了它来编写一个不太容易冻结的串行请求者(如上所述),如果它们已经等待太长时间(即排队)它会中止请求在工作线程但没有启动)。因此,如果管道堵塞并且一个请求在很长一段时间内挂起,其他请求将最多等待一段固定的时间,因此队列不会无限增长,直到阻塞消失为止。
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<State> state =
new AtomicReference<State>(State.PENDING);
Future<Response> result = executor.submit(new Callable<Response>(){
@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);
}
}
}
答案 2 :(得分:1)
gustafc是对的!要求
如果Http连接失败(例如服务器关闭),我需要递减计数器。
会破坏并发的可能性!
如果您希望HTTP标头AtomicInteger
的唯一计数器是好的,但您无法从多个服务器或JVM发送请求,并且您需要允许漏洞。
因此,由于计数是徒劳的(就像在高度可扩展的环境中一样),使用UUID
是一种更“可扩展”且更强大的解决方案。人类需要数数,机器不要该死的!
并且,如果您希望成功发送后成功的总数增加一个计数器(您甚至可以跟踪失败/成功UUID
请求)。
关于并行计数我的2 cts:)