为简单的双线程场景假设以下伪代码:
我有两个线程,我想将数据插入到数据库的不同表中。在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)
}
}
答案 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
应在finally
或run()
方法末尾的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
将失败。
正确的解决方案 如果您有多个插入任务:
ExecutorService
代替Execuutor
executorService.shutdown()
它将等待所有提交的任务完成。如果您要提交只有一项任务:
您应该在任务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(),那将阻塞当前线程,直到提交的线程完成。