0

通常、スレッドの競合をどのように処理するかについて質問があります。たとえば、Web サイトで本を販売していて、入手できる本が 1 つしかない場合などです。2 人のクライアント (スレッド) がその本を購入したいという状況があり、両方が同時に注文した場合、どのように対処すればよいでしょうか? どうすればそれを回避できますか?

より具体的には、クライアントのリクエストを受け取るサーブレットがあります。つまり、doPost メソッドを作成することはできませんsynchronized。これは、すべてのリクエスト (スレッド) が着信するときにパフォーマンスの問題が発生する可能性があるためです。

class Server{

      private Library library = Library.getInstance();   //singleton for all books

      protected void doPost(HttpServletRequest request, HttpServletResponse response) 
                                                 throws ServletException, IOException {
              synchronized(library){
                    Book book = library.get(request.getParameter("book_name"));
                    if(book != null){
                          int copies = book.getAvailable();
                          book.setAvailable(copies-1);
                          placeOrder();
                    }

              }
      }
}
4

5 に答える 5

2

このような問題に対処する最も簡単な方法は、データベースを使用して並行性を処理することです。主流のデータベースには、何年にもわたる開発努力が組み込まれた同時実行サポートが組み込まれています。データベースの ACID サポートを利用して、この種の標準的なビジネス上の問題を処理するのは比較的簡単です。

アップデート:

@MartinJames が下のコメントでよく言っているように、「データベースはすべてのものを並行して解決する」と言っているのではありません。ただし、すでにデータベースを更新しており、「トランザクション」の性質で複数ビットのデータを制御する必要がある「より長い」期間の相互作用の場合、データベースはこのレベルの同時実行性を処理するのに最適な場所です。

さらに、他のさまざまなコメントが、Java レベルの並行性を使用してメモリ内でこのデータを管理する方法を示しています。ただし、この状況では、"スケールアウト" (つまり、別のアプリ サーバーを追加する) が必要な瞬間に、Java レベルの同期が機能しなくなるため、あなたは死んでしまいます。ただし、データベース ソリューションは引き続き機能します。これが、EJB が同時実行/同期を直接使用することを思いとどまらせる理由です。代わりに、EJB/JPA の世界でのほとんどの同期はデータベースで処理されます。

于 2013-01-15T04:26:49.630 に答える
0

すべての library.get 呼び出しが (同じブックに対して) 同じオブジェクトを返す場合、そのオブジェクトを使用してコードを同期できます。ライブラリ全体よりも優れているでしょう。

    class Server{

      private Library library = Library.getInstance();   //singleton for all books

      protected void doPost(HttpServletRequest request, HttpServletResponse response) 
                                                 throws ServletException, IOException {
             boolean isOkToPlaceOrder = false;
             Book book = library.get(request.getParameter("book_name"));

             synchronized(book){
                    if (book != null) {
                          copies = book.getAvailable();
                          book.setAvailable(copies-1);
                          isOkToPlaceOrder = true;
                    }
              }
              if (isOkToPlaceOrder) placeOrder();
      }
}

一方、別のアイデアは、コードを一意の String インスタンスと同期することです (intern メソッドを使用)。

     Book book = library.get(request.getParameter("book_name"));

     synchronized(book.getISBNString().intern()){
            // reload changes made by other thread
            book = library.get(request.getParameter("book_name"));
            if (book != null) {
                  copies = book.getAvailable();
                  // if copies < 1 --> Error message

                  book.setAvailable(copies-1);

                  // save changes in synch
                  ....
                  placeOrder();
            }
      }

http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#intern() http://weblogs.java.net/blog/enicholas/archive/2006/06/all_about_inter .html

于 2013-01-15T15:14:29.100 に答える
0

さまざまな方法があります。Java でのサーバー側の処理に関しては、Java モニターを利用するのが最も簡単です。つまり、synchronized キーワードを使用して共有変数を保護します。より良い方法は、おそらく java.util.concurrent クラスを使用してプロデューサ/コンシューマ パラダイムを実装することです。たとえば、エグゼキュータで BlockingQueue を使用します。

util.concurrent クラスを使用した簡単な例を次に示します。Data クラスはカスタム クラスであることに注意してください。

    // This can go inside a main method or your controller class.
    private BlockingQueue<Data> sharedQueue = new ArrayBlockingQueue<Chunk>(BUFFER_SIZE);
    for (int i = 0; i < NUMBER_OF_PRODUCERS; i++) {
         (new Thread(new Producer(sharedQueue, i))).start();
    }

    for (int i = 0; i < NUMBER_OF_CONSUMERS; i++) {
          (new Thread(new Consumer(sharedQueue, i))).start();
    }


public class Producer implements Runnable {
    private final BlockingQueue<Data> sharedQueue;

    public Producer(BlockingQueue<Data> sharedQueue, int id) {
            this.sharedQueue = sharedQueue;
    }

    public void run() {
            try {
                 while (true) {
                       sharedQueue.put(produce());
                 }
            } catch (InterruptedException ex) {
                    ex.printStackTrace();
            }
    }

    private Data produce() { return new Data(); }
 }

public class Consumer implements Runnable {

    private final BlockingQueue<Data> sharedQueue;

    public Consumer(BlockingQueue<Data> sharedQueue, int id) {
            this.sharedQueue = sharedQueue;
    }

    public void run() {
            try {
                 while (true) {
                       consume(sharedQueue.take());
                 }
            } catch (InterruptedException ex) {
                    ex.printStackTrace();
            }
    }

    void consume(Data c) { System.out.println(c); }
}
于 2013-01-15T04:29:18.607 に答える
0

そこには同期が必要です。1 つのクライアントがスレッドで書籍の購入を開始した場合、書籍を購入したい他のクライアントは、最初のアイデアの購入またはドロップ (ロールバック) が完了するまで待機する必要があります。エンド ユーザーに示すオプションの 1 つは、それが使用されていることを示すメッセージを表示するか、ただ待つか、またはそのようなものです。実装の観点からは、Java またはデータベースでの同時実行性を知る必要があります。

于 2013-01-15T04:30:34.553 に答える
0

すでに持っているものの微調整。これは、注文プロセス全体で同期しないため、役立ちます...

class Server{

      private Library library = Library.getInstance();   //singleton for all books

      protected void doPost(HttpServletRequest request, HttpServletResponse response) 
                                                 throws ServletException, IOException {
             boolean isOkToPlaceOrder = false;
             synchronized(library){
                    Book book = library.get(request.getParameter("book_name"));
                    if (book != null) {
                          copies = book.getAvailable();
                          book.setAvailable(copies-1);
                          isOkToPlaceOrder = true;
                    }
              }
              if (isOkToPlaceOrder) placeOrder();
      }
}
于 2013-01-15T05:45:46.420 に答える