java - JDBC 在 "same"时间打开 2 个连接

标签 java mysql sql-server multithreading jdbc

我当前正在尝试同时打开 2 个 JDBC 连接,这会导致两个线程在“getConnection”调用处阻塞。 我使用自己编写的 TaskManager(这是一个带有回调的 javafx-Tasks 的简单线程池)来提交最大线程数为 8 的 TestConnectionTask。当我将最大线程数设置为 1 时,一切正常。

相关代码如下:

提交任务:

TaskManager.getInstance().submitTask(new TestConnectionTask(
    new MSSQLConnector(),
    this.dbConnectController1.getModel().getHost(),
    this.dbConnectController1.getModel().getUser(),
    this.dbConnectController1.getModel().getPassword(),
    this.dbConnectController1.getModel().getDatabase(),
    5-- timeout in seconds
), this::onDB1Connected);

TaskManager.getInstance().submitTask(new TestConnectionTask(
    new MySQLConnector(),
    this.dbConnectController2.getModel().getHost(),
    this.dbConnectController2.getModel().getUser(),
    this.dbConnectController2.getModel().getPassword(),
    this.dbConnectController2.getModel().getDatabase(),
    5-- timeout in seconds
), this::onDB2Connected);

TestConnectionTask 类:

public class TestConnectionTask extends Task<SavingDBConnector>
{
    private final DBConnector connector;
    private final String host;
    private final String user;
    private final String password;
    private final String database;
    private final int timeout;

    public TestConnectionTask(DBConnector connector, String host, String user, String password, String database, int timeout)
    {
        super();
        updateTitle("Verbindungstest");

        this.connector = connector;
        this.host = host;
        this.user = user;
        this.password = password;
        this.database = database;
        this.timeout = timeout;
    }

    @Override
    protected SavingDBConnector call() throws Exception
    {
        updateProgress(0L, 2L);
        updateMessage("Verbindungsversuch");

        try (Connection conn = this.connector.openConnection(this.host, this.user, this.password, this.database, this.timeout))
        {
            updateProgress(1L, 2L);
        }
        catch (Exception e)
        {
            updateMessage("Beim Herstellen der Verbindung ist ein Fehler aufgetreten");
            throw e;
        }

        updateProgress(2L, 2L);
        updateMessage("Verbindung erfolgreich hergstellt");

        return new SavingDBConnector(this.connector, this.host, this.user, this.password, this.database, this.timeout);
    }
}

DBConnector 类:

public abstract class DBConnector
{
    private final String jdbcClassName;
    private final String jdbcSubprotocol;
    private boolean jdbcClassLoaded;

    public DBConnector(String jdbcClassName, String jdbcSubprotocol)
    {
        this.jdbcClassName = jdbcClassName;
        this.jdbcSubprotocol = jdbcSubprotocol;
        this.jdbcClassLoaded = false;
    }

    public Connection openConnection(String host, String user, String password, String database, int timeout) throws SQLException, ClassNotFoundException
    {
        if (!this.jdbcClassLoaded)
        {
            Class.forName(this.jdbcClassName);
            this.jdbcClassLoaded = true;
        }

        Properties properties = new Properties();
        properties.setProperty("User", user);
        properties.setProperty("Password", password);

        if (timeout > 0)
        {
            properties.setProperty("LoginTimeout", Integer.toString(timeout));
        }

        Connection conn = DriverManager.getConnection(String.format("jdbc:%s://%s", this.jdbcSubprotocol, host), properties);
        conn.setCatalog(database);

        return conn;
    }
}

MSSQL 和 MySQL 的实现只是这样构建:

super("com.microsoft.sqlserver.jdbc.SQLServerDriver", "sqlserver");
and
super("com.mysql.jdbc.Driver", "mysql");

最后但并非最不重要的任务线程池,它是我的项目特定任务管理器的父类(super class)

public class TaskThreadPool
{
    private int maxThreadCount;
    private int currentThreadCount;
    private final Object lockCurrentThreadCount;
    private final ConcurrentLinkedQueue<TaskWithCallback<?>> queue;

    public TaskThreadPool()
    {
        this.maxThreadCount = 1;
        this.currentThreadCount = 0;
        this.lockCurrentThreadCount = new Object();
        this.queue = new ConcurrentLinkedQueue<>();
    }

    public void setMaxThreadCount(int maxThreadCount)
    {
        this.maxThreadCount = maxThreadCount;
    }

    public int getMaxThreadCount()
    {
        return this.maxThreadCount;
    }

    public <T> void submitTask(Task<? extends T> task, TaskCallback<T> callback)
    {
        this.queue.offer(new TaskWithCallback<>(task, callback));

        synchronized (this.lockCurrentThreadCount)
        {
            if (this.currentThreadCount < this.maxThreadCount)
            {
                this.currentThreadCount++;

                Thread thread = new Thread(this::threadRun);
                thread.setDaemon(true);
                thread.start();
            }
        }
    }

    private void threadRun()
    {
        while (true)
        {
            TaskWithCallback<?> taskWithCallback;
            while ((taskWithCallback = this.queue.poll()) != null)
            {
                taskWithCallback.run();
            }

            synchronized (this.lockCurrentThreadCount)
            {
                // Sonst möglicherweise tote Tasks
                if (this.queue.isEmpty())
                {
                    this.currentThreadCount--;
                    break;
                }
            }
        }
    }

    private class TaskWithCallback<T> implements Runnable
    {
        private final Task<? extends T> task;
        private final TaskCallback<T> callback;

        public TaskWithCallback(Task<? extends T> task, TaskCallback<T> callback)
        {
            this.task = task;
            this.callback = callback;
        }

        @Override
        public void run()
        {
            this.task.run();
            if (this.callback != null)
            {
                Platform.runLater(() -> this.callback.onTaskCompleted(this.task.getValue()));
            }
        }
    }

    public interface TaskCallback<T>
    {
        public void onTaskCompleted(T result);
    }
}

最佳答案

作为一种解决方法,我现在在 DBConnector 中使用以下同步静态方法来依次打开连接

private synchronized static Connection getConnection(String url, Properties info) throws SQLException
{
    return DriverManager.getConnection(url, info);
}

仍然期待找到一个允许我并行打开多个连接的解决方案

关于java - JDBC 在 "same"时间打开 2 个连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43914182/

相关文章:

mysql - 全新安装 VestaCP

c# - 如何将自定义函数转换为 Entity Framework Core 3.1 的 sql 表达式

javascript - 新的数据库元素只能通过刷新整个页面来显示

sql - 如何在sql server中创建依赖于其他列的默认约束

sql-server - 为什么长时间运行的查询的连接数不断增加?

java - 如何在泛型类中使用 Enum 的静态函数?

java - Spring Integration 轮询目录

java - 包含 java 中对象的二维数组的深拷贝未按预期工作

java - 使用 URL 时无法解决符号错误

mysql - 从数据库中填充数组