JDBC: Can I share a connection in a multithreading

2020-03-24 02:59发布

问题:

It seems like the classical way to handle transactions with JDBC is to set auto-commit to false. This creates a new transaction, and each call to commit marks the beginning the next transactions. On multithreading app, I understand that it is common practice to open a new connection for each thread.

I am writing a RMI based multi-client server application, so that basically my server is seamlessly spawning one thread for each new connection. To handle transactions correctly should I go and create a new connection for each of those thread ? Isn't the cost of such an architecture prohibitive?

回答1:

Yes, in general you need to create a new connection for each thread. You don't have control over how the operating system timeslices execution of threads (notwithstanding defining your own critical sections), so you could inadvertently have multiple threads trying to send data down that one pipe.

Note the same applies to any network communications. If you had two threads trying to share one socket with an HTTP connection, for instance.

  • Thread 1 makes a request
  • Thread 2 makes a request
  • Thread 1 reads bytes from the socket, unwittingly reading the response from thread 2's request

If you wrapped all your transactions in critical sections, and therefore lock out any other threads for an entire begin/commit cycle, then you might be able to share a database connection between threads. But I wouldn't do that even then, unless you really have innate knowledge of the JDBC protocol.

If most of your threads have infrequent need for database connections (or no need at all), you might be able to designate one thread to do your database work, and have other threads queue their requests to that one thread. That would reduce the overhead of so many connections. But you'll have to figure out how to manage connections per thread in your environment (or ask another specific question about that on StackOverflow).

update: To answer your question in the comment, most database brands don't support multiple concurrent transactions on a single connection (InterBase/Firebird is the only exception I know of).

It'd be nice to have a separate transaction object, and to be able to start and commit multiple transactions per connection. But vendors simply don't support it.

Likewise, standard vendor-independent APIs like JDBC and ODBC make the same assumption, that transaction state is merely a property of the connection object.



回答2:

It's uncommon practice to open a new connection for each thread. Usually you use a connection pool like c3po library.

If you are in an application server, or using Hibernate for example, look at the documentation and you will find how to configure the connection pool.



回答3:

The same connection object can be used to create multiple statement objects and these statement objects can then used by different threads concurrently. Most modern DBs interfaced by JDBC can do that. The JDBC is thus able to make use of concurrent cursors as follows. PostgreSQL is no exception here, see for example:

http://doc.postgresintl.com/jdbc/ch10.html

This allows connection pooling where the connection are only used for a short time, namely to created the statement object and but after that returned to the pool. This short time pooling is only recommended when the JDBC connection does also parallelization of statement operations, otherwise normal connection pooling might show better results. Anyhow the thread can continue work with the statement object and close it later, but not the connection.

1. Thread 1 opens statement   
3. Thread 2 opens statement
4. Thread 1 does something         Thread 2 does something
5. ...                             ...
6. Thread 1 closes statement       ...
                                7. Thread 2 closes statement

The above only works in auto commit mode. If transactions are needed there is still no need to tie the transaction to a thread. You can just partition the pooling along the transactions that is all and use the same approach as above. But this is only needed not because of some socket connection limitation but because the JDBC then equates the session ID with the transaction ID.

If I remember well there should be APIs and products around with a less simplistic design, where teh session ID and the transaction ID are not equated. In this APIs you could write your server with one single database connection object, even when it does transactions. Will need to check and tell you later what this APIs and products are.