Java Threads: Specifying what should be executed in the run function

javamultithreadingsingleton

I have a DBAdmin class that connects to the database, and then some other classes such as Article, Category and so on, that perform certain queries to a database.

I am using these classes in a Swing application to build a sort of small Intranet CMS application.

Now, the Category class has a bunch of static methods such as addCategory, editCategory and so on, like such:

public class Category implements Runnable {

    public void run () {
        //Code that will be executed in a separate thread, from the Runnable Interface
    }

    public static int addCategory(Category cat) {
        DBAdmin db = DBAdmin.getInstance();
        db.connectDB();
        //rest of the code that creates sql statement and so on...
    }

    //Other static edit and delete methods that will be called from outside
}

P.S. The DBAdmin is a Singleton Class, and the getInstance method returns the self invoked instance.

Now, what I'm trying to do is that the database actions should be run in a separate thread from the application, and I managed to do some sample tests that are run in the run method when a Thread is started.

But the problem is that I cannot specify which method should be run in the run method when a thread is started.

In the sense that when for example the addCategory is called from the outside, the run method should invoke the addCategory method inside it.

Is there a way to maybe pass a function as a callback parameter to the run method from the addCategory method so that it will know what function should be invoked in it when a new thread is started? Or is there a completely different way to achieve what I'm trying to do?

P.S. I am 'fairly' new to Java at this point, especially multi-threading, so I may be missing something here.

Best Solution

It sounds like what you want is a single thread which does all the database access for the whole program, and in that case you shouldn't be passing things to the run method. Because when you have a single thread, all it does is call a single run method once, when it starts, and that run method just keeps executing until it returns or the thread is forcibly terminated. The way to do that sort of thing is to have a synchronized queue, say, in your DBAdmin class. Then methods like addCategory would prepare their statements and add them to this shared queue. The single database thread processes the statements in the queue one at a time, and when a statement completes, it notifies the thread that added it to the queue. In fact, Java (recent versions at least) includes a set of classes that will do exactly that for you; they're called Executors and they can be found in the java.util.concurrent package. Specifically, look at the java.util.concurrent.Executors.newSingleThreadExecutor() method, which is what I'd use for a situation like this.

class SQLQueryCallable implements Callable<SQLResultSet> {
    private String query;
    public SQLQueryCallable(String query) {
        this.query = query;
    }
    public SQLResultSet Call() throws Exception {
        // execute query
        return results;
    }
}
ExecutorService ex = Executors.newSingleThreadExecutor();

// in DBAdmin somewhere:
    public SQLResultSet runQuery(String query) {
        Future<SQLResultSet> f = ex.submit(new SQLQueryCallable(query));
        return f.get(); // this waits for the query to complete
    }

If I've been misreading your question, and you actually want each database query to execute in its own thread, then I'd suggest making a class called something like SQLQuery, which implements Runnable (or extends Thread) - actually, you could even consider using java.util.concurrent.Callable, which allows you to return a value from its call() method (which is like run()). The class constructor should take an SQL statement as a parameter, and its run method should do the work to execute that statement. Then if you've used Callable, you can return the result of the statement, or if you've used Runnable, you can store it in some commonly accessible location. That's the simple answer to what you were asking about, how to pass data to a run() method: use the class constructor.

class SQLQueryRunnable implements Runnable {
    private String query;
    public SQLQueryRunnable(String query) {
        this.query = query;
    }
    public void run() {
        // execute query
    }
}

P.S. Multithreading is a hard thing to get used to but Java is a good language to do it in (my opinion).