处理并发的正确方法是什么?

时间:2014-05-02 19:39:59

标签: java java-ee servlets persistence

我有一个servlet,它通过名为DBManager的单例类访问DB。 该类包含对EntityManager的引用。 这是我的代码:

package database;


import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;

import com.btc.EMF;

import database.entities.User;



public class DBManager {
    public class UserAlreadyExistsException extends Exception{
        private static final long serialVersionUID = 1L;}
    /* attributes */
    private static DBManager instance = null;

    private final EntityManager em = EMF.get().createEntityManager();
    private int tokens = 1;
    private final EntityTransaction transaction = em.getTransaction();


    private DBManager(){
    }

    public static DBManager getInstance(){
        if(instance == null){
            instance = new DBManager();
        }
        return instance;
    }

    @SuppressWarnings("unchecked")
    public synchronized boolean createUser(String username, String password) throws UserAlreadyExistsException {
        while(tokens == 0){
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        tokens--;
        Query q = em.createQuery("select u from "+User.class.getName()+" u");
        List<User> list = q.getResultList();
        if(list != null){
            if(list.size()>0){
                throw new UserAlreadyExistsException();
            }
        }
        User u = new User();
        u.setUsername(username);
        u.setPassword(password);
        transaction.begin();
        em.persist(u);
        transaction.commit();
        tokens++;
        this.notifyAll();
        return true;
    }

    @SuppressWarnings("unchecked")
    public synchronized void eraseDB(){
        while(tokens == 0){
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        tokens--;
        Query q = em.createQuery("select u from "+User.class.getName()+" u");
        List<User> list = q.getResultList();
        transaction.begin();
        for(User u : list)
            em.remove(u);
        transaction.commit();
        tokens++;
        this.notifyAll();
    }

    public EntityManager getEm() {
        return em;
    }

}

serlvet只是按顺序调用eraseDB()createUser()

我添加了synchronized方法以及wait()notifyAll()以使并发工作但我失败了。如果我一直在浏览器中按F5,则由于并发问题会出现一些错误。此外,等待和通知的使用似乎过于复杂。

这样做的正确方法是什么?

1 个答案:

答案 0 :(得分:1)

基本上,处理Connection和相关资源的每个对象都必须在最窄的范围内初始化。除此之外,您天真地和非常为您的DBManager应用单例,这将在多个线程中使用,因为多个请求由单个servlet实例提供(有关此内容的更多信息) :How do servlets work? Instantiation, sessions, shared variables and multithreading)。

最好的选择是从DBManager类中删除单例(反)模式,并确保在应该处理数据库资源的最窄范围内初始化DBManager