由回调写入并在活动中读取的布尔值上的争用条件



从Android活动中,我发送的http请求由用户按下UI上的按钮驱动。我不希望同时运行多个请求(OutlookClient 崩溃)。

我的问题是:由于回调的结果写入在发送新请求之前读取的相同布尔值(使用 runOnUiTread),是否有可能出现竞争条件

谢谢

// Should this be either "volatile" or atomic ??
private boolean isThereAPendingRequest = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
    genericClient = clientInitializer.create(this);
    // ...
    isThereAPendingRequest = true; // still have to login
    Futures.addCallback(genericClient.logon(this, scopes), new FutureCallback<Boolean>() {
        @Override
        public void onSuccess(Boolean result) {
            // ...
            isThereAPendingRequest = false;
        }
        @Override
        public void onFailure(@NonNull Throwable t) {
            // ...
            isThereAPendingRequest = false;
        }
    });
    // ...
}
// ...
public void getBookings(View view){
    if(isThereAPendingRequest){
        Toast.makeText(getApplicationContext(), "There's already a pending request. Try in a few seconds.", Toast.LENGTH_LONG).show();
        return;
    }
    isThereAPendingRequest = true;
    Futures.addCallback( genericClient.getCalendarEvents(), new FutureCallback<List<List>>(){
        @Override
        public void onSuccess(final List<List> resultCalendars) {
            Log.d("APP", "Success. Result: "+resultCalendars);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // ..
                    isThereAPendingRequest = false;
            }
        }
    }
    // ..
}
public void sendBooking(View view){
    if(isThereAPendingRequest){
        Toast.makeText(getApplicationContext(), "There's already a pending request. Try in a few seconds.", Toast.LENGTH_LONG).show();
        return;
    }
    isThereAPendingRequest = true;
    Futures.addCallback( genericClient.sendBooking( booker, title), new FutureCallback<List<String>>(){
        @Override
        public void onSuccess(final List<String> resultBooking) {
              Log.d("APP", "Success. Result: "+resultBooking);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                      // ...
                      isThereAPendingRequest = false;
                    }
                });
        }
        @Override
        public void onFailure(Throwable t) {
           Log.e( "APP", "Delete error. Cause: "+t.getLocalizedMessage() );
           // ...
           Toast.makeText(getApplicationContext(), "Fail!", Toast.LENGTH_LONG).show();
           isThereAPendingRequest = false;
        }
   });
   }catch(Exception ex){
       // logger
       isThereAPendingRequest = false;
   }
}

更新:这是期货中调用的函数之一。

public ListenableFuture<List<List>> getCalendarEvents(){
// logger
final SettableFuture<List<List>> future = SettableFuture.create();
DateTime now = new DateTime(DateTimeZone.UTC);
DateTime workDayEnd = new DateTime( now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(), 23, 59, 0 );
Futures.addCallback(
        mClient .getMe()
                .getCalendarView()
                .addParameter("startDateTime", now)
                .addParameter("endDateTime", workDayEnd)
                .read(),
        new FutureCallback<OrcList<Event>>() {
            @Override
            public void onSuccess(final OrcList<Event> result) {
                // ...
                future.set(myList);
            }
            @Override
            public void onFailure(@NonNull Throwable t) {
                // ...
                future.setException(t);
            }
        }
);
return future;

}

如果 getBookingssetBookings 都一直在 UI 线程上调用,你应该没问题。您知道,当isThereAPendingRequest设置为 false 时,请求必须已完成,因此您可以安全离开。顺便说一下,Futures.addCallback有一个替代签名,允许您显式传入Executor,因此,如果您使用它,则无需调用runOnUiThread,从而减少了一些代码嵌套。

但是,如果您打算同时调用这些方法,我看到至少一个需要锁来防止的竞争条件。如果您有兴趣,可以了解更多详情。


为完整起见而编辑:

该问题指出,您的目标是防止两个请求同时运行。有两种情况可能会发生这种情况:

  1. isThereAPendingRequest==false,但实际上有一个待处理的请求。到目前为止,您的代码是安全的,因为您仅在请求完成后将其设置为 false。你在这里甚至不需要volatile
  2. 在不同的线程上调用getBookings和/或setBookings。如果它们同时到达if(isThereAPendingRequest)会发生什么?他们可以同时(并且正确地)看到它是假的,将其设置为 true,然后都独立发送请求并导致您崩溃。

你不需要担心 (1),(2) 应该不是问题,只要你总是在同一线程上调用这些方法。

最新更新