Java - 需要MySQL并发解决方案

时间:2014-02-20 10:36:16

标签: java mysql multithreading jdbc concurrency

我正在开发一个使用MySQL编写和读取一些数据的java应用程序。一段时间(很长一段时间)我的jdbc连接关闭,我在许多论坛上阅读,我仍然无法永远持续下去。

我想要做的是接下来的事情:在5,6,24小时后(仍然不知道多少小时)我将关闭连接并再次连接。问题是,如果另一个线程试图使用该连接写一些东西,它将在异常时失败。所以我想做的是下一件事,如果jdbc连接重新启动所有需要使用jdbc等待的线程,直到重新连接完成。此外,我不想阻止线程,如果另一个线程使用该连接,只有重新启动。我也害怕死锁:)。

我正在阅读有关java并发以及如何从许多论坛管理它,但仍然不知道使用什么以及如何使用它。

有用的论坛:

http://www.vogella.com/tutorials/JavaConcurrency/article.html

http://en.wikipedia.org/wiki/Java_concurrency

http://tutorials.jenkov.com/java-concurrency/index.html

编辑:

让我更清楚我想做什么

Java数据库类:

    public class Database {

        //Locks
        final Object readLock = new Object();
        final Object writeLock = new Object();
        final Object readWriteLock = new Object();
        boolean enableReadLock = false;
        boolean enableWriteLock = false;
        //Database parametars
        String user; //DB username /корисник на базата
        String password; //DB password /лозинка за базата
        String dbname; //DB name / име на базата
        String ip; //DB Server IP address / адреса на серверот каде е базата
        Connection connection; //mysql connection /


        public Database(String user, String password, String dbname, String ip) throws ClassNotFoundException {
            Class.forName("com.mysql.jdbc.Driver");
            this.user = user;
            this.password = password;
            this.dbname = dbname;
            this.ip = ip;
        }


        public Database(String user, String password, String dbname) throws ClassNotFoundException {
            Class.forName("com.mysql.jdbc.Driver");
            this.user = user;
            this.password = password;
            this.dbname = dbname;
            this.ip = "localhost";
        }


        public void setReadLock(boolean enable) {
            enableReadLock = enable;
        }

        /**
         * Enable or disable write lock
         *
         * @param enable true -> enable , false -> disable
         */
        public void setWriteLock(boolean enable) {
            enableWriteLock = enable;
        }

        /**
         * Enable or disable read-write lock
         *
         * @param enable true -> enable , false -> disable
         */
        public void setReadWriteLock(boolean enable) {
            enableReadLock = enable;
            enableWriteLock = enable;
        }

        public void reconnect() throws SQLException {
             connection.close();
             connection = DriverManager.getConnection("jdbc:mysql://" + ip + ":3306/" + dbname, user, password);
    }

        public void connect() throws SQLException {
            connection = DriverManager.getConnection("jdbc:mysql://" + ip + ":3306/" + dbname, user, password);
        }


        public void disconnect() throws SQLException {
            connection.close();
        }



        /**
         * Test connection with mysql server
         *
         * @return (boolean) true -> OK, false -> NOK
         */
        public boolean testConnection() {
            String SQL = "SELECT 1";
            try {
                PreparedStatement statement = connection.prepareStatement(SQL);
                ResultSet rs = statement.executeQuery();
                while (rs.next()) {
                }
            } catch (SQLException ex) {
                return false;
            } catch (NullPointerException ex){
                return false;
            }
            return true;
        }


        public ArrayList<HashMap<String, String>> executeSelect(String SQL) throws SQLException {
            if (enableReadLock && enableWriteLock) { //Доколку има read-write lock
                synchronized (readWriteLock) {
                    return select(SQL);
                }
            } else {
                if (enableReadLock) { //Доколку има read-lock
                    synchronized (readLock) {
                        return select(SQL);
                    }
                } else {
                    return select(SQL);
                }
            }
        }


        private ArrayList<HashMap<String, String>> select(String SQL) throws SQLException {
            ArrayList<HashMap<String, String>> results = new ArrayList<>();
            HashMap<String, String> row;
            PreparedStatement statement = connection.prepareStatement(SQL);
            ResultSet rs = statement.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            int no_columns = rsmd.getColumnCount();
            while (rs.next()) {
                row = new HashMap<>();
                for (int i = 1; i <= no_columns; i++) {
                    row.put(rsmd.getColumnName(i), rs.getString(i));
                }
                results.add(row);
            }
            statement.close();
            rs.close();
            return results;
        }


        public long executeInsert(String SQL) throws SQLException {
            if (enableReadLock && enableWriteLock) {
                synchronized (readWriteLock) {
                    return insert(SQL);
                }
            } else {
                if (enableWriteLock) {
                    synchronized (writeLock) {
                        return insert(SQL);
                    }
                } else {
                    return insert(SQL);
                }
            }
        }


        private long insert(String SQL) throws SQLException {
            long ID = -1;
            PreparedStatement statement = connection.prepareStatement(SQL, Statement.RETURN_GENERATED_KEYS);
            statement.executeUpdate();
            ResultSet rs = statement.getGeneratedKeys();
            if (rs.next()) {
                ID = rs.getLong(1);
            }
            statement.close();
            return ID;
        }


        public int executeUpdate(String SQL) throws SQLException {
            if (enableReadLock && enableWriteLock) {
                synchronized (readWriteLock) {
                    return update(SQL);
                }
            } else {
                if (enableWriteLock) {
                    synchronized (writeLock) {
                        return update(SQL);
                    }
                } else {
                    return update(SQL);
                }
            }
        }


        private int update(String SQL) throws SQLException {
            PreparedStatement statement = connection.prepareStatement(SQL);
            int rez = statement.executeUpdate(SQL);
            statement.close();
            return rez;
        }


        public int executeDelete(String SQL) throws SQLException {
            if (enableReadLock || enableWriteLock) {
                synchronized (readWriteLock) {
                    synchronized (readLock) {
                        synchronized (writeLock) {
                            return delete(SQL);
                        }
                    }
                }
            } else {
                return delete(SQL);
            }
        }


        private int delete(String SQL) throws SQLException {
            PreparedStatement statement = connection.prepareStatement(SQL);
            int rez = statement.executeUpdate(SQL);
            statement.close();
            return rez;
        }
    }

在重新连接方法中,我需要一些锁或某些东西,这将使每个调用select,update,insert或delete方法的人等待(阻塞),直到重新连接完成。

2 个答案:

答案 0 :(得分:1)

我认为最简单的解决方案是使用资源“连接”将所有方法标记为 synchronized 标记。 http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

例如:

Class YourClass
{
  public synchronized boolean reconect(...) { ...}
  public synchronized String getData (...) {...}
}

这会为已标记的方法锁定对象,因此您应该将连接封装在这样的小类中,或者只是锁定您的连接对象: http://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html

Class YourClass
{
  public boolean reconect(...) {
   synchronized(con) 
   {
     ...
   }
  }

  public String getData (...) {
    synchronized(con) 
    {
     ...
    }
  }
  private Connection con;
}

这些同步区域永远不会同时运行。

修改 考虑到您希望保护重新连接以防止其他数据库操作,您应该考虑使用信号量: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html

Class YourClass
{
  YourClass() {
    sem = new Semaphore(1);
  }

  public boolean reconect(...) {
     sem.acquire();
     ...
     sem.release();
  }

  public String getData (...) {
    synchronized(sem) 
    {
      if(sem.availablePermits()>0) sem.reducePermits(1);
    }
    ...
    ...
    synchronized(sem) 
    {
      sem.release();
    }
  }
  private Connection con;
  Semaphore sem;
}

答案 1 :(得分:1)

试试这个oracle jdbc multithreading tutorial.或只是用户Apache connection pool

  

多线程可以提高您的性能,但有一些   你需要知道的事情:

     

每个线程都需要自己的JDBC连接。无法共享连接   线程之间,因为每个连接也是一个事务。上传   块中的数据并偶尔提交以避免累积   巨大的回滚/撤消表。将任务分成几个工作单位   每个单位做一份工作。详细说明最后一点:目前,你   有一个任务读取文件,解析它,打开JDBC连接,   进行一些计算,将数据发送到数据库等等。

     

你应该做什么:

     

一个(!)线程来读取文件并从中创建“作业”。每份工作   应该包含一个小但不太小的“工作单元”。推那些   进入队列下一个线程等待队列中的作业并执行   计算。当步骤#1中的线程等待时,可能会发生这种情况   为慢速硬盘返回新的数据行。的结果   此转换步骤进入下一个队列中的一个或多个线程   通过JDBC上传数据。第一个和最后一个线程很漂亮   因为它们受I / O限制而变慢(硬盘速度慢,网络速度慢)   连接更糟糕)。另外,在数据库中插入数据是一种方法   非常复杂的任务(分配空间,更新索引,检查   外键)

     

使用不同的工作线程可以带来很多好处:

     

分别测试每个线程很容易。由于他们不共享数据,   你不需要同步。队列将为你做到这一点你可以   快速更改每个步骤的线程数以进行调整   性能