2016-07-29 5 views
0

私は、メソッドが(コールバックではなく)すぐにデータを必要とする(GoogleMapsタイルプロバイダの)インタフェース実装を持っているが、私がデータを取得するためにコールバックのデータを返すメソッドを呼び出さなければなりません。私はこれらの2つを結びつけなければならないし、何か(私はまだ動作していると思うがまだテストしていない)があるが、私はAndroidスタジオの警告を心配している。ローカル最終変数で同期を使用する

これは私が書いたコードです:

@Override 
public Tile getTile(int x, int y, int zoom) { 
    // If the tile provider is not available, return null 
    if(tileProvider == null) { 
     return NO_TILE; 
    } 

    // Define the tile request 
    final TileRequest tileRequest = new TileRequest(tileProvider.getLayer().getTileWidth(), tileProvider.getLayer().getTileHeight()); 

    // Make the call to get the tile data, which, depending on the situation, can possibly 
    // be handled by another thread. 
    tileProvider.getTile(x, y, zoom, new GetDataListener<byte[]>() { 
     @Override 
     public void onGetData(byte[] data, Exception exception) { 
      synchronized (tileRequest) { 
       tileRequest.data = data; 
       tileRequest.exceptionOccurred = exception != null; 
       tileRequest.finishedRequest = true; 
       tileRequest.notify(); 
      } 
     } 
    }); 

    synchronized (tileRequest) { 
     // If, before this statement was reached, the request has already been finished, call and return getTile immediately. 
     if(tileRequest.finishedRequest) { 
      return tileRequest.getTile(); 
     } else { 
      try { 
       // Wait for the tileRequest to be finished 
       tileRequest.wait(); 

       // Once it is finished (in the callback a notify is called as soon as its finished, so thats how this code is reached) 
       // the tile data is available, return the tile 
       return tileRequest.getTile(); 

      } catch(InterruptedException ex) { 
       // Exception occurred, return null so GoogleMaps will try it again later 
       logger.error("Exception in getTile method: {}", ex.getLocalizedMessage()); 
       ex.printStackTrace(); 
       return null; 
      } 
     } 
    } 
} 

だから何のAndroid Studioは、二synchronized (tileRequest) {ライン上で、私を与えていることは次の警告である:

Synchronization on local variable 'tileRequest'

Reports synchronization on a local variable or parameter. Such synchronization has little effect, since different threads usually will have different values for the local variable or parameter. The intent of the code will usually be clearer if synchronization on a field is used.

同期の私の理解にはあまりにも自信がない、待っていると通知するので、誰かが電話することができます私のアプローチが有効であれば、私は警告に関係なく有効ですか?何の複数のスレッドが関与しなかったことを示すいくつかの質問がありました

EDIT 、私はコメントを少し更新しますが、tileProvider.getTile(...)メソッドは、タイルを取得しますが、これは保証はありません別のスレッドでは発生しません。

+0

tileProvider.getTile(...)は、データを別のスレッドで取得するコールバック内のデータを返します。だからそれはマルチスレッドになります。 –

+0

どういう意味ですか?私がtileProvider.getTile(...)を持っていた理由は、リクエストを別のスレッドに委譲できるようにコールバックを使用して結果を返すためです。 –

+0

あなたは何を意味するのか分かります。 – assylias

答えて

3

ローカルオブジェクトの同期は、状況によってはJVMで無視される可能性があります。そのため、詳細な分析がなくても正しいセマンティクスを持つことはできません。あなたは、ラッチが提供する可視性の保証を失うことになるので、あなたはおそらくdone.await()if (tileRequest.finishedRequest) return tileRequest.getTile();を持つことができません

CountDownLatch done = new CountDownLatch(1); 
tileProvider.getTile(x, y, zoom, new GetDataListener<byte[]>() { 
    @Override 
    public void onGetData(byte[] data, Exception exception) { 
     tileRequest.data = data; 
     tileRequest.exceptionOccurred = exception != null; 
     tileRequest.finishedRequest = true; 
     done.countDown(); 
    } 
}); 

done.await(); 
return tileRequest.getTile(); 

注:私は単純なアプローチを示唆しています。そしてあなたのコードに基づいて、ラッチはfinishedRequestがtrueに設定された直後にブロック解除されるようです。

+0

CountDownLatchを指摘してくれてありがとう、代わりにこのコードを使用します。 –

1

2つのスレッドが同じリソースにアクセスして更新しようとしているときに、同期が必要なときに、同時に1つのスレッドしかオブジェクトにアクセスしないようにします。

tileRequestは、メソッドスコープで定義されたローカル変数です。したがって、各スレッドにはオブジェクトのインスタンスがあります。したがって、ローカル変数を同期させると、実際のマルチスレッドの利点がなくてもオブジェクトをロックするオーバーヘッドが発生します。

+0

変数がローカルであることは問題ではありませんが、それが指すオブジェクトインスタンスはメソッドに対してローカルでもあります。そうでなければ共有オブジェクトへのローカル変数が機能します。 – Thilo

+1

...このオブジェクトは、実際にGetDataListenerによって共有されているようです。 – Thilo

+0

@Thilo、ありがとうございました – sidgate

1

あなたのコードは間違いありませんが、正しいです。同期のためのメソッドローカルオブジェクトを使用していても、GetDataListenerで囲まれているため、異なるスレッドで共有される可能性があります。

CompletebleFutureをご覧ください。このコードを読みやすくするかもしれません。

+0

コードは*正しい*ではありません - ローカル変数での同期は、JVMがノーオペレーションとして扱うことができます。 – assylias

+0

@assylias:本当ですか?いつ?常に?あるいは、変数がそうでなければ共有オブジェクトを参照しないことが保証されている場合のみですか? – Thilo

+2

@Thilo(http://docs.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html#escapeAnalysis):「サーバーコンパイラによって同期ブロックが削除される可能性があります(ロックエリート)オブジェクトがスレッドローカルであると判断した場合。だから、ホットスポットサーバーコンパイラでは、バージョン> = 6u23。 –

1

IMOコンパイラの警告が熱心です。

だけでなく、技術的に間違っています。変数でJavaコードを同期することはできません。同期はオブジェクトに行うものです。

あなたがsynchronized (x) { ... }を書くとき、xは、オブジェクト参照を得るために評価され表現です。ほとんどのプログラマーは、xprivate finalインスタンス変数またはprivate final static変数であると予想しています。これらの変数は簡単に理解できますが、そのようにする必要はありません。 xはローカル変数でも、オブジェクト参照を返すメソッド呼び出しでもかまいません。

重要なことは、オブジェクトがスレッド間で共有されていることです。他のスレッドもと同じオブジェクトで同期しない限り、オブジェクト上で同期するのは意味をなさない。あなたが取得している


警告メッセージは、共通の初心者のミスを防ぐためのものです。それが技術的に間違っていても、混乱した初心者からどこかのデベロッパーヘルプセンターに毎日何十回もの電話がかかってくるのを防ぐことができます。

私が商用ソフトウェアを使用しているとき、私たちのサポートセンターへの通話数を増減するかどうかについて「正しい」か「間違っている」かどうかについては心配しません。

関連する問題