Witam,
tytułem wstępu..
ostatnio na zajęciach uczelnianych mam okazję 'zgłębiać' wiedzę w temacie XML'a (XPath, XQuery, XSLT ..). Teraz przyszedł czas na zbugowanie jakiejś aplikacji korzystającej z bazy xml, i wybór odgórnie padł na Jave, bo m.in. przykłady z których korzystamy są przygotowane pod ten język..
Wracając do wielowątkowości.. Mam następujący przykład:
package org.basex.examples.server;
import java.io.*;
import java.util.*;
import org.basex.*;
import org.basex.server.*;
/**
* This class connects multiple clients to one server instance.
* It sets up three clients with read/write access to the database.
* Database information will be shown before and after the
* clients have been run.
*
* @author BaseX Team 2005-14, BSD License
*/
public final class ServerConcurrency {
/**
* Main method of the example class.
* @param args (ignored) command-line arguments
* @throws Exception exception
*/
public static void main(final String[] args) throws Exception {
new ServerConcurrency().run();
}
/**
* Runs the example code.
* @throws Exception on error.
*/
void run() throws Exception {
System.out.println("=== ServerConcurrency ===");
// ------------------------------------------------------------------------
// Start server
System.out.println("\n* Start server.");
BaseXServer server = new BaseXServer();
// ------------------------------------------------------------------------
// Create a client session with host name, port, user name and password
System.out.println("\n* Create a client session.");
ClientSession session =
new ClientSession("localhost", 1984, "admin", "admin", System.out);
// ------------------------------------------------------------------------
// Create a database
System.out.println("\n* Create a database.");
send("CREATE DB input src/main/resources/xml/input.xml", session);
// ------------------------------------------------------------------------
// Setup some clients that simultaneously read and write from the database
System.out.println("\n* Run one reader and two writers threads.");
ClientExample reader1 = new ClientExample("//li");
ClientExample writer1 = new ClientExample("insert node " +
"<li>new node</li> as last into /html/body//ul");
ClientExample writer2 = new ClientExample("insert node " +
"<new>One more</new> as last into /html/body");
// -------------------------------------------------------------------------
// Let the clients run their query several times:
Thread.sleep(2000);
// ------------------------------------------------------------------------
// Stop the clients
System.out.println("\n* Stop client threads.");
reader1.stop();
writer1.stop();
writer2.stop();
Thread.sleep(1000);
// -------------------------------------------------------------------------
// Show modified database contents
System.out.println("\n* Show modified database contents:");
send("XQUERY //li", session);
// ----------------------------------------------------------------------
// Drop the database
System.out.println("\n* Drop the database.");
send("DROP DB input", session);
// ------------------------------------------------------------------------
// Close the client session
System.out.println("\n* Close the client session.");
session.close();
// ------------------------------------------------------------------------
// Stop the server
System.out.println("\n* Stop server.");
server.stop();
}
/**
* Processes the specified command on the server and returns the output
* or command info.
* @param cmd command to be executed
* @param cs client session reference
* @throws IOException I/O exception
*/
static void send(final String cmd, final ClientSession cs) throws IOException {
// ------------------------------------------------------------------------
// Execute the command
cs.execute(cmd);
}
/**
* This class simulates a database client.
* Within a thread, a query is run several times until the thread is stopped.
*/
private final class ClientExample {
/** Set to false to stop the Client from running.*/
boolean running = true;
/** Random sleep time generator.*/
Random r = new Random();
/**
* Constructor.
* @param query query to be run
* @throws IOException I/O exception
*/
ClientExample(final String query) throws IOException {
// ----------------------------------------------------------------------
// Create a client session and open database
final ClientSession session =
new ClientSession("localhost", 1984, "admin", "admin");
send("OPEN input", session);
// ----------------------------------------------------------------------
// Create and run the query thread
new Thread() {
@Override
public void run() {
try {
// ----------------------------------------------------------------
// Loop until the running flag has been invalidated
while(running) {
// Run the specified query
send("XQUERY " + query, session);
// Wait for a random time
Thread.sleep(r.nextInt(500));
}
// ----------------------------------------------------------------
// Close the client session
session.close();
} catch(final Exception ex) {
ex.printStackTrace();
}
}
}.start();
}
/**
* Quits the client thread.
*/
void stop() {
running = false;
}
}
}
Przykład jest w porządku. Jednak analizując go i modyfikując go do własnych 'potrzeb' napotkałem na problem - wykonując zapytania w np. 3 wątkach i 'wyrzucając' wynik do konsoli, zostają one wyświetlone w różnej kolejności..
Z tego co wiem o wątkach (a wiem niewiele) to jest to normalny stan rzeczy.. Ja ten problem 'niezdarnie' próbowałem obejść poprzez odpowiednie usypianie wątku, co z resztą jest tutaj robione i co w szczególnie określonych warunkach działa..
Jaki mam problem i co chciałbym osiągnąć?
Przykład:
Zlecam uruchomienie 10-ciu zapytań, każde w osobnym wątku. I teraz mam taką sytuację, że niektóre zapytania które wykonują się szybko zostaną wyświetlone kilka razy zanim kod 'dojdzie' do metody zatrzymującej dany wątek (bo jak widać zapytania wykonywane są w pętli -> white(running)).
Ja chciałbym oczywiście uzyskać taki efekt, że dane zapytanie jest wykonywane tylko raz przez wątek i po jednokrotnym wykonaniu zapytania ma nastąpić zakończenie tego wątku i zwrócenie wyniku.
Z tego co wiem, istnieją różne 'komendy' służące do tego typu rzeczy np. czekanie aż każdy wątek zostanie zakończony, itd.. Jednakże co ja potrzebuje zmienić/dodać w kodzie aby zaczął on działać tak jak tego oczekuje ?
ps. Ogólnie to jest 'wzorcowy' przykład z dokumentacji.. Ja już go sobie troche u siebie 'porozdzielałem' na osobne pliki, dodałem konkretne zapytania, etc.. ale to szczegóły i nie chcialem niepotrzebne zaciemniać problemu..
ps2. Teraz pisząc ten post, pomyślałem czy mógłbym np. w pętli while od razu po wykonaniu zapytania zmienić running na false.. albo w ogóle wyrzucić tą pętle i wtedy to raczej wykona się raz.. Tylko co jeśli np. 5 zapytań uruchomię w jednym wątku.. (problem raczej wróci..)
Czekam na sugestie/jakieś rozwiązania (jeżeli mógłbym prosić to chciałbym otrzymać trochę bardziej rozbudowaną i dopasowaną do wyżej przytoczonego przykładu poradę.. Bo odpowiedź w stylu "skorzystaj z metody join" nie wiele mi pomoże..)