PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Sync-Objekt blockiert, obwohl kein Thread darauf zugreift?



Technaton
24-05-2005, 13:12
Hallo,

das ganze hier ist vielleicht trivial, aber für mich gerade unlösbar.
Ich habe mir ein Programm geschrieben, daß ein ftp-Objekt erzeugt (von den Jakarta-Commons), und danach zwei Threads aufruft: Erstens einer, der 8 Sekunden schläft, dann aufwacht und ein NOOP sendet, damit die Verbindung nicht abbricht; und zweitens einer, der von System.in liest und das als Kommando an die FTP-Verbindung übermittelt.

Ohne synchronized habe ich logischerweise das Problem, daß der NOOP-Thread eine Operation unterbrechen kann, z.B. ein Verzeichnislisting. Also habe ich die entsprechenden Bereich umbaut und
synchronized(ftp) { ... } drumherumgesetzt. Jetzt habe ich das Problem, daß keines der umbauten Bereiche mehr ausgeführt wird, als würden beide darauf warten, daß der Monitor frei wird, obwohl keiner wirklich drin ist.

Das Programm macht folgendes:

CONNECT
220-FTP server ready.
220 This is a private system - No anonymous login

LOGIN
230-User blah has group access to: nobody
230 OK. Current restricted directory is /

NOOP

Das noop an sich wird aber nichtmehr ausgeführt. Weiß jemand, warum? :(

Mein Programm:


/*
* Created on 24.05.2005
*/
package testing;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.apache.commons.net.ftp.FTPClient;

public class Ftptest extends Thread {
private static String server = "xy";
private static int port = 21;
private static String user = "blah";
private static String pass = "blubb";
private static FTPClient ftp = new FTPClient();
private static BufferedReader br;
private static int reply;

private static final int LIST = 0;

static class InputListener extends Thread {
public InputListener( ) {
br =
new BufferedReader(
new InputStreamReader(System.in)
);
}

public void run() {
while(!isInterrupted()) { try {
if(ftp.isConnected()) {
String s = br.readLine();
if(s != null && !s.equals("") && !s.equals("\\n")) {
System.out.println("========== "+s+" ==========");
synchronized(ftp) {
if(s.compareToIgnoreCase("LIST") == 0) {
for(int i =0; i< ftp.listNames().length;++i)
System.out.println(s+": "+ftp.listNames()[i]);
} else { ftp.sendCommand(s);
for(int i = 0; i < ftp.getReplyStrings().length;i++)
System.out.println(s+": "+ftp.getReplyStrings()[i]);
}
ftp.notify();
}
}
}
} catch (Exception e) {
System.err.println(e.toString());
} }
}
}

static class FtpListener extends Thread {
public void run() {
while(!isInterrupted() && isAlive()) {
try {
if(!ftp.isConnected()) {
System.out.println("CONNECT");
ftp.connect(server,port);
System.out.println(ftp.getReplyString());
System.out.println("LOGIN");
ftp.login(user,pass);
System.out.println(ftp.getReplyString());
}
synchronized(ftp) {
System.out.println("NOOP");
ftp.sendNoOp();
System.out.println(ftp.getReplyString());
ftp.notify();
}
sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
try {
ftp.logout();
ftp.disconnect();
} catch (IOException ex) {
// nothing. We just ensure that no connection keeps
// alive, bumping around somewhere.
}
} catch (IOException e) {
interrupt();
}
} }
}

public static void main (String[] args) {
try {
FtpListener in = new FtpListener();
//in.setDaemon(true);
in.start();

Ftptest.InputListener inlistener = new InputListener();
inlistener.start();
in.join();
inlistener.join();

} catch (Exception e) {
e.printStackTrace();
}
}
}

anda_skoa
24-05-2005, 15:33
Passiert das auch wenn du keinen Output auf System.out machst?

Ciao,
_

RogerJFX
24-05-2005, 19:21
schreib einfach in Deine Hauptklasse


static void handleThreads (Thread thread) {

try{
thread.wait();
}
catch (IllegalMonitorStateException il) {
; // das wird vor allem am Anfang vorkommen
}
catch (Exception e) {
;
}
notifyAll();
}


Und jetzt in jeder run() - Methode der zwei Threads immer zum Schluß handleThreads(this) aufrufen.

Sollte klappen. Allerdings solltest Du gleich am Anfang (im Konstruktor) das Gleiche tun, was mindestens einmal die IllegalMonitorStateException auslösen wird, da der betreffende Thread den Monitor noch nicht besitzt. Aber Sch... drauf!

Ach so! Und schmeiß das join() raus. Braucht kein Mensch.

Cheers,

Roger

RogerJFX
24-05-2005, 19:28
join() bedeutet einfach nur, daß ein zweiter Thread drangehängt wird. Das heißt, daß der Thread, in dem join() steht, erst dann fertig ist, wenn der gejointe Thread auch durch ist. Ganz wie bei der Eisenbahn. Die Schranken gehen erst dann hoch, wenn der letzte (gejointe) Wagon vorbeigefahren ist :D .

Cheers,

Roger

RogerJFX
24-05-2005, 19:40
Oh! Jetzt habe ich nochmal genauer hingesehen.

Warum schreibst Du nicht einfach

public synchronized void run() {
while(true) { // oder sonst eine Bedingung
// mach was, und wenn es gut ist: break;
// wenn nicht, throw irgendne Exception und auch break;

???

Das "while(true)" sperrt ja dann automatisch, da die ganze Methode synchronized ist. M.E. sperrst Du schlicht zu spät. Das gibt irgendwann einen schönen Knoten in der Leitung.

Oder irre ich hier?

Cheers,

Roger

Technaton
25-05-2005, 09:54
@RogerJFX: Das Einfügen von synchronized bringt leider nichts. Deine handleThread-Routine habe ich nicht verstanden, was bewirkt die?

@anda_skoa: Ohne System.out habe ich keine Möglichkeit, das Programm zu testen -- ich habe keinen Zugriff zu irgendwelchen FTP-Logs. Ist System.out nicht threadsicher?

Ich habe noch einige Zeit rumgesucht und bin auf das hier gestoßen...

Q: Are the Commons-Net classes thread-safe? For example, can you have multiple instances of FTPClient, each with its own thread running inside?

A: Multiple instances of FTPClient can be used to connect to multiple (or the same) FTP server and concurrently retrieve files. If you want to share a single FTPClient instance between multiple threads, you must serialize access to the object with critical sections.

*kein Kommentar* :rolleyes: Ich habe jetzt eine boolean-Varibable eingeführt, die das Objekt praktisch sperrt. Wäre nur schöner, wenns mit Monitoren ginge, weil das doch eleganter anmutet...

/EDIT: Jetzt geht's ja doch! RogerJFX, dankeschön :) Ich war verwirrt, weil das Programm immer noch die NOOP-Ausgabe bringt, aber der Befehl wird nurnoch dann *wirklich* ausgeführt, wenn keine andere Operation mehr läuft. Hmtja, jetzt bin ich aber vollends verwirrt. :confused:

anda_skoa
25-05-2005, 12:43
@anda_skoa: Ohne System.out habe ich keine Möglichkeit, das Programm zu testen -- ich habe keinen Zugriff zu irgendwelchen FTP-Logs. Ist System.out nicht threadsicher?

Doch, natürlich.
Darum wäre es eben eine Quelle für ein mögliches Thread Problem.

Übrigens hast du einige Aufrufe von Methoden am FTP Objekt nicht synchronized.

Ciao,
_

Technaton
25-05-2005, 14:04
Doch, natürlich.
Darum wäre es eben eine Quelle für ein mögliches Thread Problem. Das verstehe ich nicht: Warum ist es der Grund für ein Threadproblem, wenn es doch Threadsicher ist?!


Übrigens hast du einige Aufrufe von Methoden am FTP Objekt nicht synchronized.

Ich habe jetzt, wie Roger riet, die ganze run() methode synchronisiert, jetzt geht es. Was mich nur immer noch verwirrt, ist, WARUM es jetzt geht, schließlich sind die FTP-Dinger doch angeblich nicht threadsicher. Nicht, daß ich am Ende irgendwelche Broken Pipes oder sowas habe...

anda_skoa
25-05-2005, 21:59
Das verstehe ich nicht: Warum ist es der Grund für ein Threadproblem, wenn es doch Threadsicher ist?!

Weil dann ein weiterer Monitor ins Spiel kommt.



Ich habe jetzt, wie Roger riet, die ganze run() methode synchronisiert, jetzt geht es.

Logischerweise weil die beiden Threads die nicht gemeinsam haben und jeder in seinem eigenen Monitor arbeitet :)



Was mich nur immer noch verwirrt, ist, WARUM es jetzt geht, schließlich sind die FTP-Dinger doch angeblich nicht threadsicher.

Zufall, Glück.

Gegenseitiger Ausschluß ist ja nur nötig, um einen sicheren Zustand zu garantieren, nicht unbedingt nötig um ihn zu haben, das kann auch zufällig passen.

Ich würde vorschlagen du machst die beiden Klassen nur implements Runnable.
Dann kannst du eine als extra Thread laufen lassen und eine im Hauptthread und sparst dir beim Testen die join() Hacks.

Ciao,
_

Technaton
26-05-2005, 21:47
Mit dem Threadkram bin ich immer noch nicht so ganz grün, gnah. Das synchronisieren der run()-Methoden bringt also nullgarnix? Dann doch lieber synchronisierten Zugriff auf eine Boole-Variable, die sozusagen als Sperrer fungiert... Das wird hoffentlich wohl hinhaun. :)

Das ganze Listing oben war eh nur ein einziger Test, die Grundidee ist, daß ein Thread dauernd läuft und NOOP sendet, während Einzelnkommandos dann ab und zu einstechen. Ich denke mal, daß ich das mit der Sperrvariable jetzt gelöst bekommen habe...

Danke euch. :) :o