在哪里用多个线程关闭连接/文件/日志?

时间:2014-03-03 15:59:07

标签: java multithreading threadpool threadpoolexecutor

为简单的双线程场景假设以下伪代码:

我有两个线程,我想将数据插入到数据库的不同表中。在thread1上,我想插入一些表,同时,我想将其他数据插入到线程2. 我的问题是如何/在哪里放置connection.close(),如果我放置它在线程1上执行,当thread2仍在处理时执行,反之亦然,如果thread2已经完成并关闭了连接,但是thread1还没有完成。

注意,数据库只是一个例子,它可以是文件,logger等等。

class Thread1{
    DataBaseConnection connection;
    main(){
        threadPool = Executors.newFixedThreadPool(1);
        connection.open();
        if(ThisMightTakeSomeTime)
        threadPool.submit(new MyRunnable(connection));
        InsertDataToDataBase(Table A, Table B));
        connection.Close(); //What if thread2 isn't done yet?
    }
}

public class MyRunnable implements Runnable {
    MyRunnable(connection){}
    @override
    void Run() { ...}
    void TaskThatMayTakeWhile(){
        ...get data ...
        ...Connection.InsertToTables(table X, table Y)
    }
}

3 个答案:

答案 0 :(得分:5)

  

我的问题是如何/在哪里放置connection.close(),

首先,据我所知,你应该与2个不同的线程共享一个连接。每个线程都应该拥有自己的数据库连接,可能使用数据库连接池,例如Apache's DBCP

一旦你有多个连接,我就会让每个线程管理并释放自己的连接回到池中。您应确保在finally块中完成此操作,以确保如果存在数据库异常,则仍会释放连接。

如果您被迫让多个线程共享同一个连接,那么他们必须使用synchronized来确保它们具有对它的独占锁定:

 synchronized (connection) {
    // use the connection
 }

关于何时关闭它,如果它是共享的,你可以有一个共享使用计数器(可能是AtomicInteger)并在计数器变为0时关闭它。或者正如其他人建议你可以使用一个线程-pool然后线程池完全免费连接。

  

注意,数据库只是一个例子,它可以是文件,logger等等。

就更通用的答案而言,我总是试图反映创建事物的位置。如果某个方法打开了流,那么它应该有finally来关闭流。

 public void someMethod() {
    InputStream stream = ...
    try {
         // process the stream here probably by calling other methods
    } finally {
         // stream should be closed in the same method for parity
         stream.close();
    }
 }

此模式的例外是线程处理程序。然后,Thread应在finallyrun()方法末尾的call()块中关闭流或释放连接。

 public void serverLoopMethod() {
     while (weAcceptConnections) {
        Connection connection = accept(...);
        threadPool.submit(new ConnectionHandler(connection);
     }
 }
 ...
 private static class ConnectionHandler implements Runnable {
     private Connection connection;
     public ConnectionHandler(Connection connection) {
         this.connection = connection;
     }
     // run (or call) method executed in another thread
     public void run() {
         try {
            // work with the connection probably by calling other methods
         } finally {
              // connection is closed at the end of the thread run method
              connection.close();
         }
     }
 }

答案 1 :(得分:2)

如果运行代码,可能会在执行insert语句之前关闭数据库连接,当然insert将失败。

正确的解决方案 如果您有多个插入任务

  1. 使用ExecutorService代替Execuutor
  2. 提交所有任务
  3. 调用executorService.shutdown()它将等待所有提交的任务完成。
  4. 关闭连接
  5. 如果您要提交只有一项任务: 您应该在任务Connection.InsertToTables(table X, table Y)之后关闭连接。

    适用于两种情况并建议:
    每个任务都拥有connection

    示例:

    class Thread1 {
            private static DataSource dataSource; // initialize it
    
            public static void main(String[] args){
                ExecutorService threadPool = Executors.newFixedThreadPool(1);
                threadPool.submit(new MyRunnable(dataSource));                
            }
    }
    
    class MyRunnable implements Runnable {
            private final DataSource dataSource;
    
        MyRunnable(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        public void run() {
            Connection connection = dataSource.getConnection();
            // do something with connection
            connection.close();
        }
    }
    

答案 2 :(得分:0)

class Thread1{
    DataBaseConnection connection;
    main(){
        threadPool = Executors.newFixedThreadPool(1);
        connection.open();
        if(ThisMightTakeSomeTime)
        Future f = threadPool.submit(new MyRunnable(connection));
        InsertDataToDataBase(Table A, Table B));
        f.get(); // this will hold the program until the Thread finishes.
        connection.Close(); //What if thread2 isn't done yet?
    }
}

Future是提交调用产生的引用。如果我们调用Future.get(),那将阻塞当前线程,直到提交的线程完成。