Can anyone show me a way to force one task in java to complete before the next task is allows to start? Specifically, I want to edit the code below so that the first marked two lines of code are completely finished before the next marked two lines are called.
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String idString = req.getParameter("id");
Long id = new Long(idString);
//complete the actions specified on next two lines
School school = new SchoolDAO().findSchool(id);
req.setAttribute("school", school);
//before even starting the actions specified on the next two lines
List<CourseSummary> coursesummaries = new CourseSummaryDAO().findAllcsum(id);
req.setAttribute("coursesummaries", coursesummaries);
jsp.forward(req, resp);
}
EDIT:
To better understand Fernando's suggestion, I am including some relevant parts of SchoolDAO as follows:
public class SchoolDAO extends DataAccessObject{
public School findSchool(Long id) {
ResultSet rs = null;
PreparedStatement statement = null;
Connection connection = null;
try {
connection = getConnection();
String sql = "select * from schoolprog where id=?";
statement = connection.prepareStatement(sql);
statement.setLong(1, id.longValue());
rs = statement.executeQuery();
if (!rs.next()) {return null;}
return readSchool(rs);
}
catch (SQLException e) {throw new RuntimeException(e);}
finally {close(rs, statement, connection);}
}
private School readSchool(ResultSet rs) throws SQLException {
Long id = new Long(rs.getLong("id"));
String spname = rs.getString("spname");
String spurl = rs.getString("spurl");
School school = new School();
school.setId(id);
school.setName(spname);
school.setUrl(spurl);
return school;
}
}
Similarly, CourseSummaryDAO contains:
public class CourseSummaryDAO extends DataAccessObject{
public List<CourseSummary> findAllcsum(Long sid) {
LinkedList<CourseSummary> coursesummaries = new LinkedList<CourseSummary>();
ResultSet rs = null;
PreparedStatement statement = null;
Connection connection = null;
try {
connection = getConnection(); //this is the line throwing null pointer error
String sql = "select * from coursetotals where spid=?";
statement = connection.prepareStatement(sql);
statement.setLong(1, sid);
rs = statement.executeQuery();
//for every row, call read method to extract column
//values and place them in a coursesummary instance
while (rs.next()) {
CourseSummary coursesummary = readcsum("findAll", rs);
coursesummaries.add(coursesummary);
}
return coursesummaries;
}
catch (SQLException e) {throw new RuntimeException(e);}
finally {close(rs, statement, connection);}
}
The line where the program is breaking is:
connection = getConnection(); //
If you have two tasks that should be performed serially (i.e. one finishes before the next one starts) then the best answer is to perform them synchronously. For instance, suppose that task1() and task2() are the tasks:
// Wrong way:
Runnable r1 = new Runnable(){
public void run() {
task1();
}};
Runnable r2 = new Runnable(){
public void run() {
// Wait for r1 to finish task1 ... somehow
task2();
}};
// Right way:
Runnable r = new Runnable(){
public void run() {
task1();
task2();
}};
And in your case, it looks like the doGet call can only return when it gets the result of both tasks. So that suggests that you shouldn't be using threads at all in this case. Just call task1() and task2() in sequence ... on the request thread.
EDIT
Looking at the doGet method and the two classes that you added subsequently, it looks like the processing is already sequential / serial. That is, the first "task" ends before the second "task" starts.
The problem with getConnection() throwing NullPointerException is (most likely) nothing to do with asynchrony. However I can't be sure of that without seeing the code of getConnection() and the complete stacktrace.
In Java, everything is normally executed in order, meaning that a given line of code will completely finish executing before the next line will start to do anything. The exception to this rule is when threads come into play. Threads allow multiple blocks of code to execute simultaneously. Because you aren't using any threads in your program (you'd know if you were, don't worry), it's guaranteed that the first two lines of code will complete before the next two begin to be executed.
So, your problem doesn't seem to be that your code is running "out of order". It's likely that your error is somewhere within the getConnection() method if that's what's throwing the NPE.
Here's an example (see Java Threads waiting value for details)
import java.util.concurrent.CountDownLatch;
class MyTask implements Runnable
{
CountDownLatch signal;
public MyTask(CountDownLatch signal)
{
this.signal = signal;
}
public void run()
{
System.out.println("starting task");
for (int i = 0; i < 10000000; i++)
Math.random();
//call when the task is done
signal.countDown();
}
}
public class Program
{
public static void main(String[] args) {
int workers = 1;
CountDownLatch signal = new CountDownLatch(workers);
new Thread(new MyTask(signal)).start();
try {
// Waits for all the works to finish ( only 1 in this case)
signal.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task is done");
}
}
This is just a suggestion, maybe there's a better design:
import java.util.concurrent.CountDownLatch;
public class SchoolDAO extends DataAccessObject implements Runnable {
Long id;
CountDownLatch signal;
School searchResult;
public SchoolDAO(Long id, CountDownLatch signal)
{
this.id = id;
this.signal = signal;
}
public void run()
{
searchResult = findSchool(id);
signal.countDown();
}
// the other methods didn't change
}
Now you can call it inside doGet():
CountDownLatch signal = new CountDownLatch(1);
SchoolDAO dao = new SchoolDAO(id, signal);
new Thread(dao).start();
try {
signal.await();
} catch (InterruptedException e)
{
e.printStackTrace();
}
School result = dao.searchResult;
Related
I'm wondering if anybody can help me with a rather annoying problem regarding creating a background thread in JavaFX! I currently have several SQL queries that add data to the UI which currently run on the JavaFX Application Thread (see example below). However when each of these queries execute it freezes the UI because it isn't running on a background thread. I've looked at various examples that use Task and sort of understand them but I cannot get them to work when doing database queries, some of which take a few seconds to run.
Here is one of the methods that executes a query:
public void getTopOrders() {
customerOrders.clear();
try {
Connection con = DriverManager.getConnection(connectionUrl);
//Get all records from table
String SQL = "EXEC dbo.Get_Top_5_Customers_week";
ResultSet rs;
try (Statement stmt = con.createStatement();) {
rs = stmt.executeQuery(SQL);
while (rs.next()) {
double orderValue = Double.parseDouble(rs.getString(3));
customerOrders.add(new CustomerOrders(rs.getString(1),
rs.getString(2), "£" + formatter.format(orderValue),
rs.getString(4).substring(6, 8) + "/" +
rs.getString(4).substring(4, 6) + "/" +
rs.getString(4).substring(0, 4)));
}
}
} catch (SQLException | NumberFormatException e) {
}
}
Each processed record is added to an ObservableList which is linked to a TableView, or graph or simply sets the text on a label (depends on the query). How can I execute the query on a background thread and still leave the interface free to use and be updated from the queries
Thanks in advance
I created a sample solution for using a Task (as suggested in Alexander Kirov's comment) to access a database on a concurrently executing thread to the JavaFX application thread.
The relevant parts of the sample solution are reproduced below:
// fetches a collection of names from a database.
class FetchNamesTask extends DBTask<ObservableList<String>> {
#Override protected ObservableList<String> call() throws Exception {
// artificially pause for a while to simulate a long
// running database connection.
Thread.sleep(1000);
try (Connection con = getConnection()) {
return fetchNames(con);
}
}
private ObservableList<String> fetchNames(Connection con) throws SQLException {
logger.info("Fetching names from database");
ObservableList<String> names = FXCollections.observableArrayList();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select name from employee");
while (rs.next()) {
names.add(rs.getString("name"));
}
logger.info("Found " + names.size() + " names");
return names;
}
}
// loads a collection of names fetched from a database into a listview.
// displays a progress indicator and disables the trigge button for
// the operation while the data is being fetched.
private void fetchNamesFromDatabaseToListView(
final Button triggerButton,
final ProgressIndicator databaseActivityIndicator,
final ListView listView) {
final FetchNamesTask fetchNamesTask = new FetchNamesTask();
triggerButton.setDisable(true);
databaseActivityIndicator.setVisible(true);
databaseActivityIndicator.progressProperty().bind(fetchNamesTask.progressProperty());
fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override public void handle(WorkerStateEvent t) {
listView.setItems(fetchNamesTask.getValue());
}
});
fetchNamesTask.runningProperty().addListener(new ChangeListener<Boolean>() {
#Override public void changed(ObservableValue<? extends Boolean> observable, Boolean wasRunning, Boolean isRunning) {
if (!isRunning) {
triggerButton.setDisable(false);
databaseActivityIndicator.setVisible(false);
}
};
});
databaseExecutor.submit(fetchNamesTask);
}
private Connection getConnection() throws ClassNotFoundException, SQLException {
logger.info("Getting a database connection");
Class.forName("org.h2.Driver");
return DriverManager.getConnection("jdbc:h2:~/test", "sa", "");
}
abstract class DBTask<T> extends Task<T> {
DBTask() {
setOnFailed(new EventHandler<WorkerStateEvent>() {
#Override public void handle(WorkerStateEvent t) {
logger.log(Level.SEVERE, null, getException());
}
});
}
}
// executes database operations concurrent to JavaFX operations.
private ExecutorService databaseExecutor = Executors.newFixedThreadPool(
1,
new DatabaseThreadFactory()
);
static class DatabaseThreadFactory implements ThreadFactory {
static final AtomicInteger poolNumber = new AtomicInteger(1);
#Override public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable, "Database-Connection-" + poolNumber.getAndIncrement() + "-thread");
thread.setDaemon(true);
return thread;
}
}
Note that once you start doing things concurrently, your coding and your UI gets more complicated than the default mode without Tasks when everything is single threaded. For example, in my sample I disabled the button which initiates the Task so you cannot have multiple Tasks running in the background doing the same thing (this kind of processing is similar to the web world where you might disable a form post button to prevent a form being double posted). I also added an animated progress indicator to the scene while the long running database task was executing so that the user has an indication that something is going on.
Sample program output demonstrating the UI experience when a long running database operation is in progress (note the progress indicator is animating during the fetch which means the UI is responsive though the screenshot does not show this):
To compare the additional complexity and functionality of an implementation with concurrent tasks versus an implementation which executes everything on the JavaFX application thread, you can see another version of the same sample which does not use tasks. Note that in my case with a toy, local database the additional complexity of the task based application is unnecessary because the local database operations execute so quickly, but if you were connecting to a large remote database using long running complex queries, than the Task based approach is worthwhile as it provides users with a smoother UI experience.
Managed to resolve using the solution provided by jewelsea. It is worth noting that if implementing this method when not using lists, tables and/or observable lists where you need to update an item on the UI such as a text field or label then simply add the update code within Platform.runLater. Below are some code snippets that show my working solution.
Code:
public void getSalesData() {
try {
Connection con = DriverManager.getConnection(connectionUrl);
//Get all records from table
String SQL = "EXEC dbo.Order_Information";
try (Statement stmt = con.createStatement(); ResultSet rs =
stmt.executeQuery(SQL)) {
while (rs.next()) {
todayTot = Double.parseDouble(rs.getString(7));
weekTot = Double.parseDouble(rs.getString(8));
monthTot = Double.parseDouble(rs.getString(9));
yearTot = Double.parseDouble(rs.getString(10));
yearTar = Double.parseDouble(rs.getString(11));
monthTar = Double.parseDouble(rs.getString(12));
weekTar = Double.parseDouble(rs.getString(13));
todayTar = Double.parseDouble(rs.getString(14));
deltaValue = Double.parseDouble(rs.getString(17));
yearPer = yearTot / yearTar * 100;
monthPer = monthTot / monthTar * 100;
weekPer = weekTot / weekTar * 100;
todayPer = todayTot / todayTar * 100;
//Doesn't update UI unless you add the update code to Platform.runLater...
Platform.runLater(new Runnable() {
public void run() {
todayTotal.setText("£" + formatter.format(todayTot));
weekTotal.setText("£" + formatter.format(weekTot));
monthTotal.setText("£" + formatter.format(monthTot));
yearTotal.setText("£" + formatter.format(yearTot));
yearTarget.setText("£" + formatter.format(yearTar));
monthTarget.setText("£" + formatter.format(monthTar));
weekTarget.setText("£" + formatter.format(weekTar));
todayTarget.setText("£" + formatter.format(todayTar));
yearPercent.setText(percentFormatter.format(yearPer) + "%");
currentDelta.setText("Current Delta (Week Ends): £"
+ formatter.format(deltaValue));
}
});
}
}
} catch (SQLException | NumberFormatException e) {
}
}
public void databaseThreadTester() {
fetchDataFromDB();
}
private void fetchDataFromDB() {
final testController.FetchNamesTask fetchNamesTask = new testController.FetchNamesTask();
databaseActivityIndicator.setVisible(true);
databaseActivityIndicator.progressProperty().bind(fetchNamesTask.progressProperty());
fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
}
});
fetchNamesTask.runningProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean wasRunning, Boolean isRunning) {
if (!isRunning) {
databaseActivityIndicator.setVisible(false);
}
}
;
});
databaseExecutor.submit(fetchNamesTask);
}
abstract class DBTask<T> extends Task {
DBTask() {
setOnFailed(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
}
});
}
}
class FetchNamesTask extends testController.DBTask {
#Override
protected String call() throws Exception {
fetchNames();
return null;
}
private void fetchNames() throws SQLException, InterruptedException {
Thread.sleep(5000);
getTopOrders();
getSalesData();
}
}
The only thing that doesn't appear to work with this implementation is the following, not sure why it doesn't work but it doesn't draw the graph.
public void addCricketGraphData() {
yearChart.getData().clear();
series.getData().clear();
series2.getData().clear();
try {
Connection con = DriverManager.getConnection(connectionUrl);
//Get all records from table
String SQL = "...omitted...";
try (Statement stmt = con.createStatement(); ResultSet rs =
stmt.executeQuery(SQL)) {
while (rs.next()) {
Platform.runLater(new Runnable() {
#Override
public void run() {
try {
series.getData().add(new XYChart.Data<String, Number>(rs.getString(1),
Double.parseDouble(rs.getString(7))));
series2.getData().add(new XYChart.Data<String, Number>(rs.getString(1),
Double.parseDouble(rs.getString(8))));
} catch (SQLException ex) {
Logger.getLogger(testController.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
}
} catch (SQLException | NumberFormatException e) {
}
yearChart = createChart();
}
protected LineChart<String, Number> createChart() {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
// setup chart
series.setName("Target");
series2.setName("Actual");
xAxis.setLabel("Period");
yAxis.setLabel("£");
//Add custom node for each point of data on the line chart.
for (int i = 0; i < series2.getData().size(); i++) {
nodeCounter = i;
final int value = series.getData().get(nodeCounter).getYValue().intValue();
final int value2 = series2.getData().get(nodeCounter).getYValue().intValue();
int result = value2 - value;
Node node = new HoveredThresholdNode(0, result);
node.toBack();
series2.getData().get(nodeCounter).setNode(node);
}
yearChart.getData().add(series);
yearChart.getData().add(series2);
return yearChart;
}
I want to have two threads querying (JDBC) two tables (from different servers/databases but related) for an ordered output then compare them or apply some logic record by record.
The table size can be very large so I was thinking using thread would be the most efficient way to get this done with the least footprint.
Example:
Thread1 - query table server1.database1.schema1.tableA ordered by 1;
Thread2 - query table server2.database2.schema2.tableB where [conditions/logics related to A] order by 1;
Synchronized on each record in the ResultSet in both thread and apply the comparison or data logic.
For example:
ResultSet from Thread1 = [1,2,3,4,5]
ResultSet from Thread2 = [2,4,6,8,10]
I want to be able to synchronize at each index (0...4), and compare them. Say Thread1.ResultSet[0] = Thread2.ResultSet[0]/2.
That means:
1 = 2/2
2 = 4/2
etc...
This is what I have so far, base on another answer i got while researching. I am using AtomicInteger to synchronize the ResultSet iteration.
//Main class
public class App {
public static void main(String[] args) {
try {
ReaderThread t1 = new ReaderThread();
ReaderThread t2 = new ReaderThread();
List<ReaderThread> list = new ArrayList<ReaderThread>();
list.add(t1);
list.add(t2);
HelperThread helperThread = new HelperThread(list);
helperThread.start();
t1.setName("Reader1");
t2.setName("Reader2");
t1.start();
t2.start();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//Database ReaderThread
public class ReaderThread extends Thread {
private DatabaseAccessLayer dal = new DatabaseAccessLayer(); //access layer to instantiate connection, statement and execute query and return ResultSet
private ResultSet rs;
private final Object hold = new Object();
private final AtomicInteger lineCount = new AtomicInteger(0);
private String currentLine;
public ReaderThread() throws SQLException {
this.rs = dal.executeStatement(); //execute SQL query on instantiation and get the resultset
}
#Override
public void run() {
synchronized (hold) {
try {
while (rs.next()) {
currentLine = rs.getString(1) + rs.getString(2) + rs.getString(3) + rs.getString(4)
+ rs.getString(5) + rs.getString(5);
lineCount.getAndIncrement();
System.out.println(this.getName() + " ||| " + currentLine);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public void hold () throws InterruptedException {
this.hold.wait();
}
public void release() {
this.hold.notify();
}
public boolean isLocked() {
return getState().equals(State.WAITING);
}
public Object getHold() {
return hold;
}
public AtomicInteger getLineCount() {
return lineCount;
}
public String getCurrentLine() {
return currentLine;
}
}
// THe helper class which look at two threads and determine lock conditions and subsequence logic
public class HelperThread extends Thread {
private List<ReaderThread> threads;
#Override
public void run() {
while (true) {
threads.forEach(t -> {
try {
int r1 = 0;
int r2 = 0;
//======== lock and synchronize logic here =========
if (t.getName().equals("Reader1")) r1 = t.getLineCount().get();
if (t.getName().equals("Reader2")) r2 = t.getLineCount().get();
if (t.getName().equals("Reader1") && r1 == r2) t.hold();
if (t.getName().equals("Reader2") && r2 == r1) t.hold();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
if (threads.stream().allMatch(ReaderThread::isLocked)) {
System.out.println("next line:");
threads.forEach(t -> {
synchronized (t.getLock()) {
System.out.println(t.getCurrentLine());
t.release();
}
});
System.out.println("\n");
}
}
}
public HelperThread(List<ReaderThread> threads) {
this.threads = threads;
}
}
The above code is able to execute the query concurrently on the tables and print out the resultset for each. However, the locking/holding logic is not working. I am trying to put the threads on hold when the AtomicInteger variable are the same in both thread. What this means in my code is that it will iterate through the results set one by one. For each one the AtomicInteger variable is incremented and will wait until the other thread's AtomicInteger variable to get to the same value. Then the comparison logic happens then both threads are release to move on.
I am unsure about if AtomicInteger is the correct usage here.
Any suggestion is much appreciate.
A good solution can be to use two ArrayBlockingQueue as buffers
ArrayBlockingQueue db1Buf=new ArrayBlockingQueue<>(BUF_SIZE);
ArrayBlockingQueue db2Buf=new ArrayBlockingQueue<>(BUF_SIZE);
the reading threads simply offer lines to the buffer
while (rs.next()) {
MyData data=new MyData(rs.getString(1),rs.getString(2)...);
db1Buf.offer(data); //this waits if the buffer is full
}
db1Buf.offer(null); //signal the end of table
the third thread processes data
for(;;) {
MyData db1Record=db1Buf.take();
MyData db2Record=db2Buf.take();
if (db1Record==null || db2Record==null)
break;
// do something with db1Record and db2Record
}
No synchronisation is needed because ArrayBlockingQueue is already synchronised.
the reading thread will feed the buffers and block if they are full, the third thread will consume the data waiting the other threads to read data if the buffer was empty.
The class MyData is a simple bean with the fields you need.
This question already has answers here:
JUnit terminates child threads
(6 answers)
Closed 2 years ago.
I'm currently learning JDBC. And I try to update the product information and insert a log at the same time.
private void testTransaction() {
try {
// Get Connection
Connection connection = ConnectionUtils.getConnection();
connection.setAutoCommit(false);
// Execute SQL
Product product = new Product(1, 4000d);
productService.updateProduct(connection, product);
Log log = new Log(true, "None");
logService.insertLog(connection, log);
// Commit transaction
connection.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
ConnectionUtils.closeConnection();
}
}
When using single thread, it would be fine.
#Test
public void testMultiThread() {
testTransaction();
}
But When I using multi-thread, even start one thread, the process would terminate automatically.
#Test
public void testMultiThread() {
for (int i = 0; i < 1; i++) {
new Thread(this::testTransaction).start();
}
}
After debugging, I found that it was Class.forName() function in ConnectionUtils cause this situation.
public class ConnectionUtils {
static private String url;
static private String driver;
static private String username;
static private String password;
private static Connection connection = null;
private static ThreadLocal<Connection> t = new ThreadLocal<>();
static {
try {
Properties properties = new Properties();
properties.load(new FileReader("src/main/resources/jdbcConnection.properties"));
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driver);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
try {
connection = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
} finally {
t.set(connection);
}
return connection;
}
}
The process will terminate at Class.forName(). I found this by adding two print funcion before and after the statement. And only the former works.
System.out.println("Before");
Class.forName(driver);
System.out.println("After");
The console only print the Before and doesn't show any exception information.
I want to know that why multi-thread in java will cause this situation and how to solve this problem.
This is more likely your test method complete before your other threads and the test framework is not waiting (junit?). You need to wait until the threads have completed. You should use an Executors, this is more convinient.
#Test
public void testMultiThread() {
Thread[] threads = new Thread[1];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(this::testTransaction);
threads[i].start();
}
// wait thread completion
for (Thread th : threads) {
th.join();
}
}
Junit will terminate all your thread as long as the test method finish.
In your case, test will finish when the loop ends, it doesn't care whether
testTransaction has finished. It has nothing to do with class.forName , maybe it's just because this method exceute longer.
you can check this answer
I'm wondering if anybody can help me with a rather annoying problem regarding creating a background thread in JavaFX! I currently have several SQL queries that add data to the UI which currently run on the JavaFX Application Thread (see example below). However when each of these queries execute it freezes the UI because it isn't running on a background thread. I've looked at various examples that use Task and sort of understand them but I cannot get them to work when doing database queries, some of which take a few seconds to run.
Here is one of the methods that executes a query:
public void getTopOrders() {
customerOrders.clear();
try {
Connection con = DriverManager.getConnection(connectionUrl);
//Get all records from table
String SQL = "EXEC dbo.Get_Top_5_Customers_week";
ResultSet rs;
try (Statement stmt = con.createStatement();) {
rs = stmt.executeQuery(SQL);
while (rs.next()) {
double orderValue = Double.parseDouble(rs.getString(3));
customerOrders.add(new CustomerOrders(rs.getString(1),
rs.getString(2), "£" + formatter.format(orderValue),
rs.getString(4).substring(6, 8) + "/" +
rs.getString(4).substring(4, 6) + "/" +
rs.getString(4).substring(0, 4)));
}
}
} catch (SQLException | NumberFormatException e) {
}
}
Each processed record is added to an ObservableList which is linked to a TableView, or graph or simply sets the text on a label (depends on the query). How can I execute the query on a background thread and still leave the interface free to use and be updated from the queries
Thanks in advance
I created a sample solution for using a Task (as suggested in Alexander Kirov's comment) to access a database on a concurrently executing thread to the JavaFX application thread.
The relevant parts of the sample solution are reproduced below:
// fetches a collection of names from a database.
class FetchNamesTask extends DBTask<ObservableList<String>> {
#Override protected ObservableList<String> call() throws Exception {
// artificially pause for a while to simulate a long
// running database connection.
Thread.sleep(1000);
try (Connection con = getConnection()) {
return fetchNames(con);
}
}
private ObservableList<String> fetchNames(Connection con) throws SQLException {
logger.info("Fetching names from database");
ObservableList<String> names = FXCollections.observableArrayList();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select name from employee");
while (rs.next()) {
names.add(rs.getString("name"));
}
logger.info("Found " + names.size() + " names");
return names;
}
}
// loads a collection of names fetched from a database into a listview.
// displays a progress indicator and disables the trigge button for
// the operation while the data is being fetched.
private void fetchNamesFromDatabaseToListView(
final Button triggerButton,
final ProgressIndicator databaseActivityIndicator,
final ListView listView) {
final FetchNamesTask fetchNamesTask = new FetchNamesTask();
triggerButton.setDisable(true);
databaseActivityIndicator.setVisible(true);
databaseActivityIndicator.progressProperty().bind(fetchNamesTask.progressProperty());
fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override public void handle(WorkerStateEvent t) {
listView.setItems(fetchNamesTask.getValue());
}
});
fetchNamesTask.runningProperty().addListener(new ChangeListener<Boolean>() {
#Override public void changed(ObservableValue<? extends Boolean> observable, Boolean wasRunning, Boolean isRunning) {
if (!isRunning) {
triggerButton.setDisable(false);
databaseActivityIndicator.setVisible(false);
}
};
});
databaseExecutor.submit(fetchNamesTask);
}
private Connection getConnection() throws ClassNotFoundException, SQLException {
logger.info("Getting a database connection");
Class.forName("org.h2.Driver");
return DriverManager.getConnection("jdbc:h2:~/test", "sa", "");
}
abstract class DBTask<T> extends Task<T> {
DBTask() {
setOnFailed(new EventHandler<WorkerStateEvent>() {
#Override public void handle(WorkerStateEvent t) {
logger.log(Level.SEVERE, null, getException());
}
});
}
}
// executes database operations concurrent to JavaFX operations.
private ExecutorService databaseExecutor = Executors.newFixedThreadPool(
1,
new DatabaseThreadFactory()
);
static class DatabaseThreadFactory implements ThreadFactory {
static final AtomicInteger poolNumber = new AtomicInteger(1);
#Override public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable, "Database-Connection-" + poolNumber.getAndIncrement() + "-thread");
thread.setDaemon(true);
return thread;
}
}
Note that once you start doing things concurrently, your coding and your UI gets more complicated than the default mode without Tasks when everything is single threaded. For example, in my sample I disabled the button which initiates the Task so you cannot have multiple Tasks running in the background doing the same thing (this kind of processing is similar to the web world where you might disable a form post button to prevent a form being double posted). I also added an animated progress indicator to the scene while the long running database task was executing so that the user has an indication that something is going on.
Sample program output demonstrating the UI experience when a long running database operation is in progress (note the progress indicator is animating during the fetch which means the UI is responsive though the screenshot does not show this):
To compare the additional complexity and functionality of an implementation with concurrent tasks versus an implementation which executes everything on the JavaFX application thread, you can see another version of the same sample which does not use tasks. Note that in my case with a toy, local database the additional complexity of the task based application is unnecessary because the local database operations execute so quickly, but if you were connecting to a large remote database using long running complex queries, than the Task based approach is worthwhile as it provides users with a smoother UI experience.
Managed to resolve using the solution provided by jewelsea. It is worth noting that if implementing this method when not using lists, tables and/or observable lists where you need to update an item on the UI such as a text field or label then simply add the update code within Platform.runLater. Below are some code snippets that show my working solution.
Code:
public void getSalesData() {
try {
Connection con = DriverManager.getConnection(connectionUrl);
//Get all records from table
String SQL = "EXEC dbo.Order_Information";
try (Statement stmt = con.createStatement(); ResultSet rs =
stmt.executeQuery(SQL)) {
while (rs.next()) {
todayTot = Double.parseDouble(rs.getString(7));
weekTot = Double.parseDouble(rs.getString(8));
monthTot = Double.parseDouble(rs.getString(9));
yearTot = Double.parseDouble(rs.getString(10));
yearTar = Double.parseDouble(rs.getString(11));
monthTar = Double.parseDouble(rs.getString(12));
weekTar = Double.parseDouble(rs.getString(13));
todayTar = Double.parseDouble(rs.getString(14));
deltaValue = Double.parseDouble(rs.getString(17));
yearPer = yearTot / yearTar * 100;
monthPer = monthTot / monthTar * 100;
weekPer = weekTot / weekTar * 100;
todayPer = todayTot / todayTar * 100;
//Doesn't update UI unless you add the update code to Platform.runLater...
Platform.runLater(new Runnable() {
public void run() {
todayTotal.setText("£" + formatter.format(todayTot));
weekTotal.setText("£" + formatter.format(weekTot));
monthTotal.setText("£" + formatter.format(monthTot));
yearTotal.setText("£" + formatter.format(yearTot));
yearTarget.setText("£" + formatter.format(yearTar));
monthTarget.setText("£" + formatter.format(monthTar));
weekTarget.setText("£" + formatter.format(weekTar));
todayTarget.setText("£" + formatter.format(todayTar));
yearPercent.setText(percentFormatter.format(yearPer) + "%");
currentDelta.setText("Current Delta (Week Ends): £"
+ formatter.format(deltaValue));
}
});
}
}
} catch (SQLException | NumberFormatException e) {
}
}
public void databaseThreadTester() {
fetchDataFromDB();
}
private void fetchDataFromDB() {
final testController.FetchNamesTask fetchNamesTask = new testController.FetchNamesTask();
databaseActivityIndicator.setVisible(true);
databaseActivityIndicator.progressProperty().bind(fetchNamesTask.progressProperty());
fetchNamesTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
}
});
fetchNamesTask.runningProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean wasRunning, Boolean isRunning) {
if (!isRunning) {
databaseActivityIndicator.setVisible(false);
}
}
;
});
databaseExecutor.submit(fetchNamesTask);
}
abstract class DBTask<T> extends Task {
DBTask() {
setOnFailed(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
}
});
}
}
class FetchNamesTask extends testController.DBTask {
#Override
protected String call() throws Exception {
fetchNames();
return null;
}
private void fetchNames() throws SQLException, InterruptedException {
Thread.sleep(5000);
getTopOrders();
getSalesData();
}
}
The only thing that doesn't appear to work with this implementation is the following, not sure why it doesn't work but it doesn't draw the graph.
public void addCricketGraphData() {
yearChart.getData().clear();
series.getData().clear();
series2.getData().clear();
try {
Connection con = DriverManager.getConnection(connectionUrl);
//Get all records from table
String SQL = "...omitted...";
try (Statement stmt = con.createStatement(); ResultSet rs =
stmt.executeQuery(SQL)) {
while (rs.next()) {
Platform.runLater(new Runnable() {
#Override
public void run() {
try {
series.getData().add(new XYChart.Data<String, Number>(rs.getString(1),
Double.parseDouble(rs.getString(7))));
series2.getData().add(new XYChart.Data<String, Number>(rs.getString(1),
Double.parseDouble(rs.getString(8))));
} catch (SQLException ex) {
Logger.getLogger(testController.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
}
} catch (SQLException | NumberFormatException e) {
}
yearChart = createChart();
}
protected LineChart<String, Number> createChart() {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
// setup chart
series.setName("Target");
series2.setName("Actual");
xAxis.setLabel("Period");
yAxis.setLabel("£");
//Add custom node for each point of data on the line chart.
for (int i = 0; i < series2.getData().size(); i++) {
nodeCounter = i;
final int value = series.getData().get(nodeCounter).getYValue().intValue();
final int value2 = series2.getData().get(nodeCounter).getYValue().intValue();
int result = value2 - value;
Node node = new HoveredThresholdNode(0, result);
node.toBack();
series2.getData().get(nodeCounter).setNode(node);
}
yearChart.getData().add(series);
yearChart.getData().add(series2);
return yearChart;
}
I have created a database object according to the singleton pattern. The database object contains 2 methods: connect() and update().
The update should run multithreaded, meaning that I cannot put synchronized in the update method signature (I want users to access it simultaneously not one at a time).
My problem is that I want to make sure that 2 scenarios according to this flows:
A thread 1 (user1) is the first to create an instance of the DB and thread 2 (user2) is calling the connect() and update() method to this DB - should not give NullPointerException even if by the time that user2 is doing the update() the connect from user1 is not done.
update() should not include synchronized (because of the reason I mentioned above).
Thanks for all the helpers!
SingeltonDB
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SingeltonDB {
private static DBconnImpl db = null;
private static SingeltonDB singalDb = null;
Lock dbLock;
private SingeltonDB(String username, String password) {
db = new DBconnImpl();
}
public static boolean isOpen() {
return (db != null);
}
public synchronized static SingeltonDB getInstance(String username,
String password) throws Exception {
if (db != null) {
throw (new Exception("The database is open"));
} else {
System.out.println("The database is now open");
singalDb = new SingeltonDB(username, password);
}
db.connect(username, password);
System.out.println("The database was connected");
return singalDb;
}
public synchronized static SingeltonDB getInstance() throws Exception {
if (db == null) {
throw (new Exception("The database is not open"));
}
return singalDb;
}
public void create(String tableName) throws Exception {
dbLock = new ReentrantLock();
dbLock.lock();
db.create(tableName);
dbLock.unlock();
}
public User query(String tableName, int rowID) throws Exception {
if (db == null) {
System.out.println("Error: the database is not open");
return null;
}
return (db.query(tableName, rowID));
}
public void update(String tableName, User user) throws Exception {
if (db == null) {
System.out.println("Error: the database is not open");
return;
}
db.update(tableName, user);
}
}
Main
public class Main {
public static void main(String[] args) throws Exception {
Creator cr= new Creator(new UserContorller());
Thread t1 = new Thread(cr);
t1.start();
Producer pr = new Producer(new UserContorller());
Thread t2 = new Thread(pr);
t2.start();
/*
* Consumer cn = new Consumer(new UserContorller()); Thread t2 = new
* Thread(cn); t2.start();
*/
}
}
class Creator implements Runnable {
UserContorller uc;
public Creator(UserContorller uc) {
this.uc = uc;
}
#Override
public void run() {
try {
uc = new UserContorller("MyAccount", "123");
uc.createTable("table1");
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Producer implements Runnable {
UserContorller uc;
public Producer(UserContorller uc) {
this.uc = uc;
}
#Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
uc.saveUser("table1", i, "User", i);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
UserContorller uc;
public Consumer(UserContorller uc) {
this.uc = uc;
}
#Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println(uc.getUser("table1", i));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Note: The post below was written in the perspective of both users using the same credentials (hidden from them) to connect to the database. If users employ different credentials, the idea of a singleton db object is purposeless, each user should have their own connection object, and of course then connection details are passed on from the user to the Db via whatever represents the user in the program (here the thread instances apparently).
The main issue in the implementation you provided is that the getinstance method requires its caller to know the connection details, or assume that the connection has already been done. But neither threads could nor should know in advance if the Db has been opened already -- and design wise it's a mistake to hand them the responsibility of explicitely opening it. These threads are work threads, they shouldn't be concerned about Db configuration details.
The only sane way to handle this situation is to have these configuration parameters held by the Db object directly, or better yet another object in charge of providing it (it's the factory pattern).
However, if you want first your code to work with minimal changes, get rid of the parameter less getinstance method, have any thread requiring the Db object to use the remaining variant of that method, passing along the correct parameters, and change it to return the instance if it exists, or create it otherwise, without raising an exception. I believe that it's what #Dima has been trying to explain in his answer.
Connect once, when creating the singleton (in the constructor, perhaps).
Have a synchronized static method (getInstance or something), that checks if an instance exists, creates and connects as necessary, and returns the instance. By following this protocol, you ensure that threads always get a connected Db object ready to be used.
The users will call that method to get the singleton instance, and call update or whatever they want on it, it does not need to be synchronized.