0

我有一个关于我们通常如何处理线程冲突的问题?例如,如果您在网站上销售一本书,但只有一本可用。情况是有2个客户(线程)想买那本书,碰巧两个客户同时下单,那我们怎么处理呢?我们如何避免它?

更具体地说,我有一个接收客户端请求的 servlet,我的意思是因为我们不能只创建 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

有多种方式。就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

在那里你需要同步。如果一个客户已经开始在线程中购买这本书,另一个想要购买这本书的客户应该等到第一个完成购买或放弃想法(回滚)。向最终用户指示的一种选择是仅显示正在使用它的消息,等待或类似的东西。从实现的角度来看,您需要了解 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 回答
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 回答