I want to use Executors to speed up the import of a list of available data to the database
public static void main(String[] args) {
methodOpenDatabaseConnection();
ExecutorService newFixedThreadPool = Executors.newScheduledThreadPool(100);
for (long i = 0; i < 1000; i++) {
newFixedThreadPool.execute(new Runnable() {
#Override
public void run( {
methodSaveDataToDatabaseMysql();
}
});
}
newFixedThreadPool.shutdown();
newFixedThreadPool.shutdown();
newFixedThreadPool.awaitTermination(60, TimeUnit.SECONDS);
if (newFixedThreadPool.isShutdown()) {
methodCloseConnectionDatabase();
}
}
public void methodSaveDataToDatabaseMysql() {
try {
preparedStatement = connection.prepareStatement("INSERT INTO `demo`"
+ " (`name`,`name2`,`name3`) VALUES (?,?,?)");
preparedStatement.setString(1, "name1");
preparedStatement.setString(2, "name2");
preparedStatement.setString(3, "name3");
preparedStatement.executeUpdate();
} catch (SQLException ex) {
Logger.getLogger(WebsiteResourcesImpl1.class.getName()).log(Level.SEVERE, null, ex);
}
}
It generated an error,If I add synchronized in the save method.It will succeed but the speed does not improve
java.sql.SQLException: No value specified for parameter 3
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:569)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:537)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:527)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:512)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:480)
at com.mysql.cj.jdbc.PreparedStatement.checkAllParametersSet(PreparedStatement.java:2159)
I think there is no problem with the save statement, because it can still save data successfully but not stable in ExecutorService
Based upon this code preparedStatement = connection.prepareStatement... then preparedStatement is not thread safe.
Consider to make it local to the methodSaveDataToDatabaseMysql method
Related
The answer to the following described issue may be as simple as that I am not using SortedSet correctly, but I wouldn't know if that is the case.
void SQLRankGuildsByPoints(final CallbackReturnIntegerStringSortedSet callback)
{
java.sql.Connection cn = null;
try {
cn = DataSource.getConnection();
if(cn != null)
{
PreparedStatement query = cn.prepareStatement("SELECT GuildName, TotalActivityPoints FROM Guilds");
ResultSet result = query.executeQuery();
SortedSet<Pair_IntString> GuildsRanking = new TreeSet(new ComparatorGuildsRanking());
while(result.next())
{
int resultInt = result.getInt("TotalActivityPoints");
String resultString = result.getString("GuildName");
GuildsRanking.add(new Pair_IntString(resultInt, resultString));
}
Bukkit.getScheduler().runTask(MainClassAccess, new Runnable() { //Callback to main thread
#Override
public void run() {
callback.onDone(GuildsRanking);
}
});
}
} catch (SQLException e) {
System.err.print(e);
} finally {
try {
cn.close();
} catch (SQLException e) {
System.err.print(e);
}
}
}
All 8 results from the Guilds table are present in "result" ResultSet.
GuildsRanking.add() isn't adding the new custom Pair_IntString object constructed with the query results, specifically for guilds "test" and "lilo" in Guilds table.
SQLRankGuildsByPoints method finishes it's execution, calling back the GuildsRanking SortedSet without 2 of the iterated results.
This behaviour is unintended and I can't find an explanation for it.
The comparator used for TreeSet:
public class ComparatorGuildsRanking implements Comparator<Pair_IntString> {
#Override
public int compare(Pair_IntString intStr1, Pair_IntString intStr2) {
return intStr2.integer.compareTo(intStr1.integer);
}
}
Custom Pair_IntString class:
public class Pair_IntString {
public Integer integer;
public String string;
Pair_IntString(Integer i, String s)
{
integer = i;
string = s;
}
}
No error messages with the skipped add iterations.
I am using Java 1.8 and H2 1.4.199.
I have a method insertRecord(DATA_OBJECT) which inserts single row into table, and then returns the generated ID for this row. It instantiates a Task object and submit it onto SingleThreadExecutorService one-by-one, and the generated ID is retrieved from Future object. Most of the time it works just fine. However, sometimes a situation like this happens:
Row 10 insert is submitted to ExecutorService
Row 11 insert is meant to be waiting in front of "synchronized" block to be submitted to ExecutorService
Row 10 is being inserted by code, and Future returns generated ID = 11
Row 11 is being inserted by code, and Future returns generated ID = 11
I have no working and reproducible example, because it is a very rare situation, but it happens.
No exceptions thrown and I have no idea how this could happen.
Below is the example of code:
private static final ExecutorService singleExecutor = Executors.newSingleThreadExecutor();
private static String insertRecord(DATA_OBJECT dataObject) {
Future<String> future;
synchronized (singleExecutor) {
future = singleExecutor.submit(new Task(dataObject));
}
try {
return future.get();
} catch (InterruptedException ex) {
Logger.getLogger(CLASS_NAME.class.getName()).log(Level.SEVERE, null, ex);
} catch (ExecutionException ex) {
Logger.getLogger(CLASS_NAME.class.getName()).log(Level.SEVERE, null, ex);
}
}
private class Task implements Callable<String> {
private DATA_OBJECT dataObject;
public Task(DATA_OBJECT dataObject) {
this.dataObject = dataObject;
}
#Override
public String call() {
try {
return execute(dataObject);
} catch (SQLException ex) {
Logger.getLogger(CLASS_NAME.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
}
private static String execute(DATA_OBJECT dataObject) {
Connection conn = Database.getTransactedConnection();
String lastId = null;
boolean success = false;
try (
PreparedStatement statement =
conn.prepareStatement(
"insert into TABLE (COLUMN_NAMES) values (VALUES)",
Statement.RETURN_GENERATED_KEYS
)
) {
statement.setString(1, dataObject.STRING_1);
statement.setString(N, dataObject.STRING_N);
success = statement.executeUpdate() == 1;
if (success) {
try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
if (generatedKeys.next()) {
lastID = generatedKeys.getString(1);
}
}
}
} finally {
if (success)
conn.commit();
else
conn.rollback();
Database.releaseConnection(conn);
}
return lastId;
}
I am trying to find a way to avoid 100% of these events because the consequences are devastating. How can I do this?
What could be the problem leading to the return of the identifier from the future?
I'm creating an Android application where I've a login system using MySql. I'm using a Thread for the login.
private static boolean isValid = false;
public static boolean login(final String username, final String password) {
new Thread(new Runnable() {
#Override
public void run() {
try{
Class.forName("com.mysql.jdbc.Driver");
Connection con= DriverManager.getConnection(
"jdbc:mysql://*****/***","*****","******");
Statement stmt=con.createStatement();
String query = "SELECT passHash FROM test WHERE username = '"+ username +"'";
ResultSet rs=stmt.executeQuery(query);
System.out.println(query + " <----");
while(rs.next()) {
isValid = BCrypt.checkpw(password, rs.getString(1));
System.out.println(isValid + "<.daspdfsafpa");
}
System.out.println(isValid +" 1");
con.close();
System.out.println(isValid +" 2");
}
catch(Exception e)
{
System.out.println(e);
}
}
}).start();
try{
Thread.currentThread().join();
}
catch(InterruptedException e){
System.out.println(e.getMessage());
}
System.out.println("blafasf");
return isValid;
}
When I'm using the Thread.currentThread.join(); the app never runs the return isValid;. So my method will never get returned when I'm using the .join() , and if I remove it the method will be returned before the thread is done. How can I fix this issue?
I want the method to run through the thread before returning the isValid in the bottom of the method.
Any ideas?
I'm still looking for a solution on this. How can I make this is another way to still make it work?
I am trying to run few queries using a multithreaded approach, however I think I am doing something wrong because my program takes about five minute to run a simple select statement like
SELECT * FROM TABLE WHERE ID = 123'
My implementation is below and I am using one connection object.
In my run method
public void run() {
runQuery(conn, query);
}
runQuery method
public void runQuery(Connection conn, String queryString){
Statement statement;
try {
statement = conn.createStatement();
ResultSet rs = statement.executeQuery(queryString);
while (rs.next()) {}
} catch (SQLException e) {
e.printStackTrace();
}
}
Finally in the main method, I start the threads using the snippet below.
MyThread bmthread = new MyThread(conn, query);
ArrayList<Thread> allThreads = new ArrayList<>();
double start = System.currentTimeMillis();
int numberOfThreads = 1;
for(int i=0; i<=numberOfThreads; i++){
Thread th = new Thread(bmthread);
th.setName("Thread "+i);
System.out.println("Starting Worker "+th.getName());
th.start();
allThreads.add(th);
}
for(Thread t : allThreads){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
double end = System.currentTimeMillis();
double total = end - start;
System.out.println("Time taken to run threads "+ total);
Update : I am now using separate connection for each thread.
ArrayList<Connection> sqlConn = new ArrayList<>();
for(int i =0; i<10; i++){
sqlConn.add(_ut.initiateConnection(windowsAuthURL, driver));
}
loop:
MyThread bmthread = new MyThread(sqlConn.get(i), query);
As rohivats and Asaph said, one connection must be used by one and only one thread, that said, consider using a database connection pool. Taking into account that c3p0, DBCP and similars are almost abandoned, I would use HikariCP which is really fast and reliable.
If you want something very simple you could implement a really simple connection pool using a thread safe collection (such as LinkedList), for example:
public class CutrePool{
String connString;
String user;
String pwd;
static final int INITIAL_CAPACITY = 50;
LinkedList<Connection> pool = new LinkedList<Connection>();
public String getConnString() {
return connString;
}
public String getPwd() {
return pwd;
}
public String getUser() {
return user;
}
public CutrePool(String connString, String user, String pwd) throws SQLException {
this.connString = connString;
for (int i = 0; i < INITIAL_CAPACITY; i++) {
pool.add(DriverManager.getConnection(connString, user, pwd));
}
this.user = user;
this.pwd = pwd;
}
public synchronized Connection getConnection() throws SQLException {
if (pool.isEmpty()) {
pool.add(DriverManager.getConnection(connString, user, pwd));
}
return pool.pop();
}
public synchronized void returnConnection(Connection connection) {
pool.push(connection);
}
}
As you can see getConnection and returnConnection methods are synchronized to be thread safe. Get a connection (conn = pool.getConnection();) and don't forget to return/free a connection after being used (pool.returnConnection(conn);)
Don't use the same connection object in all threads. Give each thread a dedicated database connection.
One Connection can only execute one query at a time. You need multiple connections available to execute database operations in parallel. Try using a DataSource with a connection pool, and make each thread request a connection from the pool.
This is the code that I am using to implement the queue.
Here queue poll is always returning null even when queue is not empty.
Runnable runnable = new Runnable() {
#Override
public void run() {
service.schedule(runnable, 500, TimeUnit.MILLISECONDS);
process();
}
public void process() {
try {
String tt = nextItem();
//System.out.println("SQ:"+tt);
} catch (Exception e) {//Catch exception if any
System.out.println("2Error: " + e.getMessage());
}
}
};
public String nextItem() {
Object poll;
try {
synchronized (queue) {
System.out.println("SQ:" + queue.poll());
//if (poll != null) {
// return poll.toString();
//} else {
return "";
//}
}
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
public void run() {
try {
Class.forName("com.mysql.jdbc.Driver");
String url =
"jdbc:mysql://1xxx:3306/ayan";
Connection con =
DriverManager.getConnection(
url, "[user]", "[pass]");
Queue queue = new LinkedList();
service = Executors.newScheduledThreadPool(1000);
service.schedule(runnable, 0, TimeUnit.MILLISECONDS);
while (true) {
Statement statement = con.createStatement();
statement.setFetchSize(1);
ResultSet resultSet = statement.executeQuery("SELECT * from query_q");
while (resultSet.next()) {
// process results. each call to next() should fetch the next row
String id = resultSet.getString("id");
String query = resultSet.getString("query");
String msisdn = resultSet.getString("msisdn");
String pass = id + "|" + query + "|" + msisdn;
System.out.println("MQ:" + pass);
//String str = "foo";
//Queue<Character> charsQueue = new LinkedList<Character>();
boolean inserted = false;
for (char c : pass.toCharArray()) {
inserted = queue.offer(c);
}
if (inserted != false) {
// Statement stats = con.createStatement();
//stats.executeUpdate("delete from query_q where id=" + id);
}
}
Thread.sleep(10000);
}
//con.close();
}
LinkedList is the only non-thread safe Queue. Any other implementation would have been a better choice. Your offer is not synchronized. ;)
An ExecutorService has a built in queue. You can make use of that and not create a queue of your own at all. Just execute(Runnable) tasks as you need something to be done.
That's because you are not synchronizing the queue for your queue.offer(). You need to synchronize all access to the queue.
The simplest way to do this is to use a LinkedBlockingQueue which will take care of all the synchronization for you.
Note that you call offer() and poll() on different queue's - offer()'s queue is a local variable, whereas the poll()'s one is probably a field:
Queue queue = new LinkedList();
Also, syncrhonization is required, as suggested in other answers.