java—atomicinteger是为多线程应用程序提供计数器的好解决方案吗?

wgx48brx  于 2021-06-30  发布在  Java
关注(0)|答案(3)|浏览(287)

我有一个android客户端,它将与服务器建立http连接。
服务器要求所有http请求在http头中提供一个单调递增的计数器。例如

POST /foo/server
X-count: 43

将启动http连接的位置:
用户命令下的内部活动,例如按钮单击
服务内部(由 Context#startService )
为了提出反价值观,我计划举办一个 AtomicInteger 在我的 Application 子类。然后,所有代码将从中心位置检索计数。
如果http连接失败(例如服务器关闭),我需要减少计数器。
你认为atomicinteger适合我的场景吗?

elcex8rz

elcex8rz1#

古斯塔夫是对的!要求
如果http连接失败(例如服务器关闭),我需要减少计数器。
杀死任何并发的可能性!
如果您想要http头的唯一计数器 AtomicInteger 很好,但是不能从多个服务器或jvm发送请求,需要允许漏洞。
所以,由于使用 UUID 是一个更具“可扩展性”和健壮性的解决方案。人类需要数数,机器一点也不在乎!
因此,如果您希望成功发送的总数在成功发送后增加一个计数器(您甚至可以跟踪失败/成功) UUID 请求)。
我的两个CT并行计数:)

jjhzyzn0

jjhzyzn02#

atomicinteger正是您要用于此目的的。

lf3rwulv

lf3rwulv3#

如果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 很好,但在这种情况下是不正确的。
edit2我的目的是编写一个串行请求程序(如上面的一个)不太容易冻结,这样如果请求挂起时间过长(即,在工作线程中排队但未启动),它就会中止请求。因此,如果管道阻塞并且一个请求挂起很长时间,其他请求最多会等待一段固定的时间,因此在阻塞消失之前,队列不会无限增长。

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

}

相关问题