Java JDBC查询在单独线程中锁定父级

6

我有些难以理解这个问题。

事情是这样的,我正在创建一个新的线程,它持有一个连接到Oracle数据库的JDBC连接。

当我请求它连接到数据库时,父线程继续运行,同时启动start()方法,但是当我要求子线程执行查询(在另一个方法中),父线程会被卡住,等待子线程的方法完成工作。

有什么解决方法吗?

谢谢!

public class Main extends Thread{

    public Main()
    {
    }

    public void myCounter() {
        int i = 0;
        DBConnection myConnection = null;
        for(;;)
        {
            i++;

            System.out.println("time: " + i);
            if( i  == 5)
            {
                myConnection = new DBConnection("localhost", 1521, "hr", "hr", "XE");
                myConnection.start();


            }
            if(i == 10)
                try {

                    myConnection.runQuery("Select * from hr.numbers order by dbms_random.value");
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void run()
    {
        myCounter();
    }

    public static void main(String[] args) {

        Main boot = new Main();
        boot.start();

    }

}

public class DBConnection extends Thread{

    Connection myConnection;
    int port;
    String user;
    String password;
    String serviceName;
    String host;


    public void run()
    {
        setUpConnection(host, port, user, password, serviceName);
    }
    /**
     * Sets up variables to create a connection to Oracle.
     *  
     * @param host      host
     * @param port      port
     * @param user      user
     * @param password  password
     */
    public DBConnection(String host, int port, String user, String password, String serviceName)
    {
        this.host = host;
        this.port = port;
        this.user = user;
        this.password = password;
        this.serviceName = serviceName;
    }


    private void setUpConnection(String host, int port, String user,
            String password, String dataBase) {
        System.out.println("-------- Oracle "
                + "JDBC Connection Testing ------------");

        try {

            Class.forName("oracle.jdbc.OracleDriver");

        } catch (ClassNotFoundException e) {

            System.out.println("Couldn't find Oracle JDBC Driver... :-(");
            e.printStackTrace();
            return;

        }

        System.out.println("Oracle JDBC Driver Registered!");
        myConnection = null;
        try {
            myConnection = DriverManager.getConnection(
                    "jdbc:oracle:thin:@//" 
                            + host 
                            + ":" 
                            + port 
                            + "/" 
                            + dataBase,
                            user, password
                    );

        } catch (SQLException e) {

            System.out.println("Connection Failed!");
            e.printStackTrace();
            return;

        }

        if (myConnection != null) {
            System.out.println("Connected to Oracle! :-)");
        } else {
            System.out.println("Failed to make connection!");
        }
    }


    /**
     * Queries the database and returns a ResultSet
     * @param  query            SQL
     * @throws SQLException 
     */
    public  ResultSet runQuery(String query) throws SQLException 
    {
        System.out.println("                                                    [DBConnection] Started Running @ " + (new SimpleDateFormat("HH:mm:ss:S")).format(new Date()));
        ResultSet rs = null;

        Statement stt = myConnection.createStatement();
        rs = stt.executeQuery(query);
        System.out.println("                                                    [DBConnection] Finished Running @: " + (new SimpleDateFormat("HH:mm:ss:S")).format(new Date()));
        return  rs;

    }

这是我得到的输出结果:
time: 1
time: 2
time: 3
time: 4
time: 5
-------- Oracle JDBC Connection Testing ------------
Oracle JDBC Driver Registered!
time: 6
Connected to Oracle! :-)
time: 7
time: 8
time: 9
time: 10
                                                    [DBConnection] Started Running @ 14:46:00:660
                                                    [DBConnection] Finished Running @: 14:46:12:750
time: 11
time: 12
time: 13
time: 14

... ... .. .


顺便说一下,我特意创建了包含100万条记录的hr.numbers表,以便它需要很长时间才能完成。 - Yaroze
你正在主线程上调用runQuery方法。语法真的没有帮助,但是你的myConnection是一个新线程并不意味着你对runQuery的调用会在一个新线程中执行。你必须在run方法中不断检查一个标志以启动查询(在myConnection上),并在父级上简单地标记它。类似于if(flag){runQuery();} else {spleep(1000)}。 - Plínio Pantaleão
3个回答

6

我认为您对线程的工作方式有些误解。问题在于您正在父线程上调用此函数:

myConnection.runQuery("Select * from hr.numbers order by dbms_random.value");

这是对myConnection对象进行的一系列顺序调用runQuery方法,此时该对象是一个线程。这并不意味着它会指示子线程执行该方法。相反,父线程会自己执行它,并且子线程将在其run方法返回后立即结束。
如果您想要一个单独的线程来持续接收要执行查询的命令,则需要实现生产者-消费者模式,其中父线程将命令排队等待子线程执行。为此,建议您查看ExecutorService

是的,这非常有意义。你觉得执行以下操作怎么样:创建另一个线程,仅处理发给DBConnection对象的请求?像Main创建一个DBHandler对象。另一方面,DBHandler创建一个DBConnection对象,该对象有效地查询数据库。因此,Main将向DBHandler请求内容,而DBHandler会等待DBConnection返回结果。你觉得呢? - Yaroze
当然我会看一下ExecutorService。如果有什么可以简化我的工作,我会非常乐意使用它。但是我仍然想找出是否有某种解决方法来处理这个问题 :-) - Yaroze
@user885878:是的,那样可以。你只需要想办法将查询传递给子线程进行执行即可。一个简单的方法是使用Executor.newSingleThreadExecutor()。这将有效地创建一个子线程,它会不断轮询任务队列(所有操作都在后台完成)。在这些任务中,你可以放置查询语句,子线程会自动执行它们。 - Tudor
太好了!非常感谢你的帮助!我现在就去看一下!祝你有愉快的一天! - Yaroze
你也一样,祝你好运! - Tudor

2
当你调用myConnection.runQuery()时,你位于父线程的run()方法内部,因此即使你调用其方法之一,也不在connection线程中。
为了达到你想要的效果,应该在myConnectionrun()方法内部调用runQuery

1
你的runQuery方法是阻塞的,因为你在主线程上调用它(所以它会等待查询完成)。 如果你想将其调用到另一个线程,请调用其他线程的run方法(只需在原本调用查询方法的地方启动另一个线程即可)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接