Warten bis Variabel nicht null ist

  • Themenstarter Themenstarter mickedplay
  • Beginndatum Beginndatum
M

mickedplay

Gast
Hallöle,

Ich habe ein"kleines Problem".
[Java] Thread - Pastebin.com

Und zwar möchte ich etwas abrufen, und zwar so, dass der Mainthread nicht behindert wird. Also hab ich nen neuen Thread benutzt.
Jetzt hab ich nun dass Problem, dass er immer ne NullPointerException wirft (Zeile 62 des Pastebincodes).

Ich weiß auch nicht, wie ich das wirklich erklären soll.

Ich möchte einfach nur eine neue Variable erstellen (Zeile 62). Jedoch ist es immer "null", da es ein wenig dauert, bis er zum Internet verbunden hat. Währenddessen macht der weiter.


Hoffe, ihr versteht, was ich will. xD
 
Du hast das hier versucht?

Java:
String uuid = new DataFetcher(args[0]).getUUID().toString();

Und du wunderst dich, dass das eine NPE ergibt?

Mal abgesehen, dass der Code Müll ist. Denn man verwendet bei Threads kein thread.stop(); sondern bricht sie höfflichst mit interrupts ab: HIER.

Nun zu deinem Problem:
Du verwendest den Konstruktor, wo der name gesetzt wird und uuid bleibt null!

Darum entweder in diesem Konstruktor auch die uuid setzen oder den zweiten Konstruktor aufrufen!
 
Du hast das hier versucht?

Java:
String uuid = new DataFetcher(args[0]).getUUID().toString();

Und du wunderst dich, dass das eine NPE ergibt?

Mal abgesehen, dass der Code Müll ist. Denn man verwendet bei Threads kein thread.stop(); sondern bricht sie höfflichst mit interrupts ab: HIER.

Nun zu deinem Problem:
Du verwendest den Konstruktor, wo der name gesetzt wird und uuid bleibt null!

Darum entweder in diesem Konstruktor auch die uuid setzen oder den zweiten Konstruktor aufrufen!

Nein? Die UUID ermittel ich doch aus dem Namen bzw. umgekehrt. In der run()-Methode.
 
Wow sorry. My bad.

Ja das ist klar, wenn das länger dauert, dann brauchst natürlich einen join, der dir auf das Ergebnis wartet, oder einen Callback (also Listener) der dich dann informiert, wann etwas da ist.
 
So um meinen Fauxpas von vorher wett zu machen, hab ich dir mal ein kleines KSKB zusammengestellt wie sowas normalerweise gemacht werden kann.

Java:
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class Test {
  public static void main(String... args) {
    UUIDFetcher fetch1 = new UUIDFetcher("test");
    try {
      TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println(fetch1.getUUID());
    System.out.println(fetch1.getUUID());
    
    UUIDFetcher fetch2 = new UUIDFetcher("test2");
    System.out.println(fetch2.getUUID());
  }
  
  static class UUIDFetcher {
    private ExecutorService executor;
    
    private Future<UUID> uuid;
    private String name;
    
    public UUIDFetcher(String name) {
      this.name = name;
      executor = Executors.newSingleThreadExecutor();
      uuid = resolve();
    }
    
    private Future<UUID> resolve() {
      return executor.submit(new Callable<UUID>() {
        @Override
        public UUID call() {
          try {
            TimeUnit.SECONDS.sleep(3);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          executor.shutdown();
          return UUID.randomUUID();
        }
      });
    }
    
    public UUID getUUID() {
      try {
        return uuid.get();
      } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
      }
      return null;
    }
    
    public String getName() {
      return name;
    }
  }
}
 
Wow sorry. My bad.

Ja das ist klar, wenn das länger dauert, dann brauchst natürlich einen join, der dir auf das Ergebnis wartet, oder einen Callback (also Listener) der dich dann informiert, wann etwas da ist.
Hab's mal mit nem join() ausprobiert, jedoch hält trotzdem alles an. :/
 
So jetzt zu meinem zweiten Vorschlag mit einem Listener:

Java:
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Test implements UUIDListener {
  public static void main(String... args) {
    new UUIDFetcher("test", new Test());
  }
  
  static class UUIDFetcher {
    private ExecutorService executor;
    
    private UUID uuid;
    private UUIDListener listener;
    private String name;
    
    public UUIDFetcher(String name, UUIDListener listener) {
      this.name = name;
      this.listener = listener;
      executor = Executors.newSingleThreadExecutor();
      resolve();
    }
    
    private void resolve() {
      executor.execute(new Runnable() {
        @Override
        public void run() {
          try {
            TimeUnit.SECONDS.sleep(3);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          uuid = UUID.randomUUID();
          listener.resolved(name, uuid);
          executor.shutdown();
        }
      });
    }
    
    public UUID getUUID() {
      return uuid;
    }
    
    public String getName() {
      return name;
    }
  }
  
  @Override
  public void resolved(String name, UUID uuid) {
    System.out.println(name + ": " + uuid);
  }
}

interface UUIDListener {
  void resolved(String name, UUID uuid);
}

Zwei Möglichkeiten und du darfst dir jetzt eine aussuchen. Bei Fragen: Immer her damit!
 
Zuletzt bearbeitet:
So jetzt zu meinem zweiten Vorschlag mit einem Listener:

Java:
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Test implements UUIDListener {
  public static void main(String... args) {
    new UUIDFetcher("test", new Test());
  }
  
  static class UUIDFetcher {
    private ExecutorService executor;
    
    private UUID uuid;
    private UUIDListener listener;
    private String name;
    
    public UUIDFetcher(String name, UUIDListener listener) {
      this.name = name;
      this.listener = listener;
      executor = Executors.newSingleThreadExecutor();
      resolve();
    }
    
    private void resolve() {
      executor.execute(new Runnable() {
        @Override
        public void run() {
          try {
            TimeUnit.SECONDS.sleep(3);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          uuid = UUID.randomUUID();
          listener.resolved(name, uuid);
          executor.shutdown();
        }
      });
    }
    
    public UUID getUUID() {
      return uuid;
    }
    
    public String getName() {
      return name;
    }
  }
  
  @Override
  public void resolved(String name, UUID uuid) {
    System.out.println(name + ": " + uuid);
  }
}

interface UUIDListener {
  void resolved(String name, UUID uuid);
}

Zwei Möglichkeiten und du darfst dir jetzt eine aussuchen. Bei Fragen: Immer her damit!

Sorry, entweder funktioniert es nicht, oder ich mach's falsch.

Also...
Ich bin ein Spieler auf einem Server. Dort spielen auch andere Spieler. Nun gebe ich einen Befehl ein. Der Befehl verbindet zum Internet. Während er verbindet, stehen alle Spieler still. Dies soll aber nicht sein. Und genau dies ist mein Problem.
 
Du setzt deinen Befehl ab, der einige Zeit in Anspruch nimmt. Das in einen separaten Thread auslagern ist schon mal ok. Aber der Thread muss sich nach Beendigung auch melden: Ich bin fertig.
Das funktioniert nun mal mit Callback/Listener.

Was heißt das, dass funktioniert bei mir nicht? Was funktioniert nicht?
Threads richtig gestartet, richtig den Callback implementiert?
 
Zuletzt bearbeitet:
Du setzt deinen Befehl ab, der einige Zeit in Anspruch nimmt. Das in einen separaten Thread auslagern ist schon mal ok. Aber der Thread muss sich nach Beendigung auch melden: Ich bin fertig.
Das funktioniert nun mal mit Callback/Listener.

Was heißt das, dass funktioniert bei mir nicht? Was funktioniert nicht?
Threads richtig gestartet, richtig den Callback implementiert?

Mich verwirrt das alles ein bisschen langsam.
So habe ich es aufgerufen:
Java:
Fetcher uuid = new Fetcher(args[0]);
						sender.sendMessage(API.prefix + "UUID von '" + args[0] + "' ist: " + uuid.getUUID());

Fetcher-Klasse:
Java:
package play.mickedplay.api.thread;

import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import play.mickedplay.api.utils.UUIDFetcher;
 
public class Fetcher
{
    private ExecutorService executor;
 
    private Future<UUID> uuid;
    private String name;
 
    public Fetcher(String name)
    {
    	this.name = name;
    	executor = Executors.newSingleThreadExecutor();
    	uuid = resolve();
    }
	 
    private Future<UUID> resolve()
    {
    	return executor.submit(new Callable<UUID>()
    	{
    		@Override
    		public UUID call()
    		{
    			try{TimeUnit.MILLISECONDS.sleep(3);}
    			catch(InterruptedException e){e.printStackTrace();}
    			executor.shutdown();
    			return UUIDFetcher.getUUIDOf(name);
    		}
    	});
    }
	 
    public UUID getUUID()
    {
    	try{return uuid.get();}
    	catch(InterruptedException|ExecutionException e){e.printStackTrace();}
    	return null;
    }
 
    public String getName()
    {
    	return name;
    }
}

Es ist aber genauso wie vorher. Alles bleibt nebenbei hängen.
 
Ja ist klar. Da du auf das Ergebnis wartest, da die uuid-Variable eine Future ist und wenn du die get-Methode aufrufst, wartet er bis das Ergebnis geliefert ist.

Also darum nimm die zweite Variante. Warte nicht auf des Ergebnis sondern lass dir das Ergebnis liefern!
 
Ja ist klar. Da du auf das Ergebnis wartest, da die uuid-Variable eine Future ist und wenn du die get-Methode aufrufst, wartet er bis das Ergebnis geliefert ist.

Also darum nimm die zweite Variante. Warte nicht auf des Ergebnis sondern lass dir das Ergebnis liefern!
Die zweite Variante funktioniert teilweise.

Wie ich das ganze aufrufe:
Java:
UUIDFetcher fetcher = new UUIDFetcher(args[0], new Fetcher());
sender.sendMessage(API.prefix + "UUID von '" + args[0] + "' ist: " + fetcher.getUUID());

UUIDFetcher-Klasse:
Java:
package play.mickedplay.api.thread;

import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import play.mickedplay.api.utils.IDFetcher;
 
public class Fetcher implements UUIDListener
{
    public static class UUIDFetcher
    {
        private ExecutorService executor;
        
        private UUID uuid;
        private UUIDListener listener;
        private String name;
    
        public UUIDFetcher(String name, UUIDListener listener)
        {
            this.name = name;
            this.listener = listener;
            executor = Executors.newSingleThreadExecutor();
            resolve();
        }
        
        private void resolve()
        {
            executor.execute(new Runnable()
            {
                @Override
                public void run()
                {
                    uuid = IDFetcher.getUUIDOf(name);
                    System.out.println("Ready");
                    listener.resolved(name, uuid);
                    executor.shutdown();
                }
            });
        }
        
        public UUID getUUID()
        {
            return uuid;
        }
        
        public String getName() 
        {
            return name;
        }
    }
    
    @Override
    public void resolved(String name, UUID uuid)
    {
        System.out.println(name + ": " + uuid);
    }
}

interface UUIDListener
{
    void resolved(String name, UUID uuid);
}
Das "Ready" erscheint wie es soll, nachdem etwas ermittelt wurde, in der Konsole.
Nur beim Aufrufen, im oberen Code, wird die untere Zeile aufgerufen, bevor das darüber nochnichtmal fertig ist.
 
Zuletzt bearbeitet von einem Moderator:
Wie ich sehe verwendest du das KSKB, dass ich dir geliefert habe. Das heißt auch, dass du es adaptieren musst. Nimm dir bitte auch mal Stift und Papier zur Hand und male den Ablauf, wie dein Programm funktionieren soll.

Zu deinem Problem:

Natürlich springt er gleich zur nächsten Anweisung, wenn du einen Thread spawnst, dass soll auch so sein!
Darum alles was NACH dem erstellen deines Threads kommen soll, sollte von der Callback-Methode (Listener) aufgerufen werden, oder sogar in der Callback-Methode behandelt werden.

Ich schreib dir jetzt nocheinmal ein KSKB, dann ist genug der Hilfe, denn Verständnis kann man nicht zeigen, sondern muss man erlangen.

Java:
import java.util.concurrent.TimeUnit;

public class Test implements FinishedListener {
  public static void main(String... args) {
    Test t = new Test();
    t.calculate(1, 2);
  }
  
  public void calculate(int a, int b) {
    System.out.format("Now calculating: %d + %d: ", a, b);
    new Thread(new LazyAdder(this, a, b)).start();
    for (int i = 0; i < 10; i++) {
      System.out.print('.');
      try {
        TimeUnit.MILLISECONDS.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
  
  @Override
  public void finished(int sum) {
    System.out.format("%nFinished calculation: %d%n", sum);
  }
}

class LazyAdder implements Runnable {
  private FinishedListener listener;
  private int a, b;
  
  public LazyAdder(FinishedListener listener, int a, int b) {
    this.listener = listener;
    this.a = a;
    this.b = b;
  }
  
  @Override
  public void run() {
    try {
      TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    listener.finished(a + b);
  }
  
}

interface FinishedListener {
  void finished(int sum);
}
 
Wie ich sehe verwendest du das KSKB, dass ich dir geliefert habe. Das heißt auch, dass du es adaptieren musst. Nimm dir bitte auch mal Stift und Papier zur Hand und male den Ablauf, wie dein Programm funktionieren soll.

Zu deinem Problem:

Natürlich springt er gleich zur nächsten Anweisung, wenn du einen Thread spawnst, dass soll auch so sein!
Darum alles was NACH dem erstellen deines Threads kommen soll, sollte von der Callback-Methode (Listener) aufgerufen werden, oder sogar in der Callback-Methode behandelt werden.

Ich schreib dir jetzt nocheinmal ein KSKB, dann ist genug der Hilfe, denn Verständnis kann man nicht zeigen, sondern muss man erlangen.

Java:
import java.util.concurrent.TimeUnit;

public class Test implements FinishedListener {
  public static void main(String... args) {
    Test t = new Test();
    t.calculate(1, 2);
  }
  
  public void calculate(int a, int b) {
    System.out.format("Now calculating: %d + %d: ", a, b);
    new Thread(new LazyAdder(this, a, b)).start();
    for (int i = 0; i < 10; i++) {
      System.out.print('.');
      try {
        TimeUnit.MILLISECONDS.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
  
  @Override
  public void finished(int sum) {
    System.out.format("%nFinished calculation: %d%n", sum);
  }
}

class LazyAdder implements Runnable {
  private FinishedListener listener;
  private int a, b;
  
  public LazyAdder(FinishedListener listener, int a, int b) {
    this.listener = listener;
    this.a = a;
    this.b = b;
  }
  
  @Override
  public void run() {
    try {
      TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    listener.finished(a + b);
  }
  
}

interface FinishedListener {
  void finished(int sum);
}
Ich glaube du verstehst nicht, was ich will.
Wenn in deiner run()-Methode hier 5 SECONDS steht, geht es doch garnicht. Vielleicht braucht der Abrufvorgang ausm Internet länger?
 
Hast du diese kleine Testklasse schon ausprobiert? Hast du den Code verstanden?

Man kann auch 1 Stunde statt 5 Sekunden nehmen und es würde genau das machen, was ich bereits oben erwähnt habe.
 
Hast du diese kleine Testklasse schon ausprobiert? Hast du den Code verstanden?

Man kann auch 1 Stunde statt 5 Sekunden nehmen und es würde genau das machen, was ich bereits oben erwähnt habe.

Ich hab's eben mit einer Stunde probiert, und nachdem die 10 Punkte erschienenpassierte nichts. Weil er ja eben 1 Stunde wartet.
Ich beschäftige mich mit Threads erst seit ein paar Tagen, tut mir Leid.
 
Nö du schaust dir den Code nicht an! Die 10 punkte passieren im Hauptthread und dann kommt der Abfragethread.
 
Alles, was in der run-()Methode von "LazyAdder" ist, läuft dann parallel zum Hauptthread ab, richtig? Wenn ja, wie kann ich denn dort Variablen bzw. Werte definieren und von anderen Klassen darauf zugreifen?
 
Genau, alles in der run() ist parallel.

Objekt(e) im Konstruktor mitgeben. Ich fürchte dir fehlen wichtige Grundlagen. Ich empfehle dir diverse Tutorials zu lesen/schauen.
 

Zurück
Oben