Czy ktoś może mi zasugerować, jak mogę przekazać parametr do wątku?
Jak to działa w przypadku anonimowych zajęć?
Consumer<T>
.
Czy ktoś może mi zasugerować, jak mogę przekazać parametr do wątku?
Jak to działa w przypadku anonimowych zajęć?
Consumer<T>
.
Odpowiedzi:
Musisz przekazać parametr w konstruktorze do obiektu Runnable:
public class MyRunnable implements Runnable {
public MyRunnable(Object parameter) {
// store parameter for later user
}
public void run() {
}
}
i wywołaj to w ten sposób:
Runnable r = new MyRunnable(param_value);
new Thread(r).start();
r
będzie miał ten sam argument, więc jeśli chcemy przekazać różne argumenty do wielu działających wątków MyThread
, musimy utworzyć nową MyThread
instancję, używając żądanego argumentu dla każdego wątku. Innymi słowy, aby uruchomić wątek, musimy utworzyć dwa obiekty: Thread i MyThread. Czy jest to uważane za złe pod względem wydajności?
W odpowiedzi na edycję pytania o to, jak to działa dla klas Anonimowych
final X parameter = ...; // the final is important
Thread t = new Thread(new Runnable() {
p = parameter;
public void run() {
...
};
t.start();
Masz klasę, która rozszerza wątek (lub implementuje Runnable) oraz konstruktor o parametrach, które chcesz przekazać. Następnie, gdy tworzysz nowy wątek, musisz przekazać argumenty, a następnie rozpocząć wątek, mniej więcej tak:
Thread t = new MyThread(args...);
t.start();
Runnable jest znacznie lepszym rozwiązaniem niż Thread BTW. Wolałbym więc:
public class MyRunnable implements Runnable {
private X parameter;
public MyRunnable(X parameter) {
this.parameter = parameter;
}
public void run() {
}
}
Thread t = new Thread(new MyRunnable(parameter));
t.start();
Ta odpowiedź jest w zasadzie taka sama jak w przypadku podobnego pytania: Jak przekazać parametry do obiektu Thread
parameter
bezpośrednio z run()
metody bez użycia pola jak p
w ogóle. Wydaje się, że działa. Czy brakuje mi subtelnej wielowątkowości, parameter
której p
wcześniej nie kopiowałem ?
)
ci pierwszego przykładu
final X parameter
przed new Runnable()
linią, miałem dostęp do parameter
środka run()
. Nie musiałem robić dodatkowych rzeczy p = parameter
.
final
nie jest już tak ważny; wystarczy, jeśli zmienna jest faktycznie ostateczna (mimo że nie szkodzi jej posiadanie)
za pomocą konstruktora klasy Runnable lub Thread
class MyThread extends Thread {
private String to;
public MyThread(String to) {
this.to = to;
}
@Override
public void run() {
System.out.println("hello " + to);
}
}
public static void main(String[] args) {
new MyThread("world!").start();
}
@Override
za?
@Override
wyraźnie stwierdza, że zastępuje metodę abstrakcyjną w klasie Thread.
Ta odpowiedź przychodzi bardzo późno, ale może ktoś uzna ją za przydatną. Chodzi o to, jak przekazać parametr (y) do Runnable
nawet bez deklarowania nazwanej klasy (przydatne dla wkładek):
String someValue = "Just a demo, really...";
new Thread(new Runnable() {
private String myParam;
public Runnable init(String myParam) {
this.myParam = myParam;
return this;
}
@Override
public void run() {
System.out.println("This is called from another thread.");
System.out.println(this.myParam);
}
}.init(someValue)).start();
Oczywiście możesz przełożyć wykonanie start
na bardziej dogodny lub odpowiedni moment. I to od Ciebie zależy, jaki będzie podpisinit
metody (może więc wymagać więcej i / lub różnych argumentów) i oczywiście nawet jej nazwy, ale w zasadzie masz pomysł.
W rzeczywistości istnieje również inny sposób przekazania parametru do anonimowej klasy za pomocą bloków inicjalizujących. Rozważ to:
String someValue = "Another demo, no serious thing...";
int anotherValue = 42;
new Thread(new Runnable() {
private String myParam;
private int myOtherParam;
{
this.myParam = someValue;
this.myOtherParam = anotherValue;
}
@Override
public void run() {
System.out.println("This comes from another thread.");
System.out.println(this.myParam + ", " + this.myOtherParam);
}
}).start();
Wszystko dzieje się więc w bloku inicjalizacyjnym.
this.myParam
wtedy naprawdę jest to konieczne? Czy nie można po prostu porzucić zmiennych prywatnych i odwołać się do zmiennej z zakresu zewnętrznego? Rozumiem (oczywiście), że ma to pewne implikacje, takie jak że zmienna jest otwarta na zmianę po uruchomieniu wątku.
Podczas tworzenia wątku potrzebujesz wystąpienia Runnable
. Najprostszym sposobem na przekazanie parametru byłoby przekazanie go jako argumentu do konstruktora:
public class MyRunnable implements Runnable {
private volatile String myParam;
public MyRunnable(String myParam){
this.myParam = myParam;
...
}
public void run(){
// do something with myParam here
...
}
}
MyRunnable myRunnable = new myRunnable("Hello World");
new Thread(myRunnable).start();
Jeśli następnie chcesz zmienić parametr podczas działania wątku, możesz po prostu dodać metodę ustawiającą do klasy uruchamialnej:
public void setMyParam(String value){
this.myParam = value;
}
Gdy to zrobisz, możesz zmienić wartość parametru, wywołując w ten sposób:
myRunnable.setMyParam("Goodbye World");
Oczywiście, jeśli chcesz wywołać akcję po zmianie parametru, będziesz musiał użyć blokad, co znacznie komplikuje sytuację.
Aby utworzyć wątek, zwykle tworzysz własną implementację Runnable. Przekaż parametry do wątku w konstruktorze tej klasy.
class MyThread implements Runnable{
private int a;
private String b;
private double c;
public MyThread(int a, String b, double c){
this.a = a;
this.b = b;
this.c = c;
}
public void run(){
doSomething(a, b, c);
}
}
Możesz rozszerzyć lub i podać parametry, jak chcesz. Istnieją proste przykłady w docs . Przeniesię je tutaj:Thread
class
Runnable
class
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
PrimeThread p = new PrimeThread(143);
p.start();
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
Napisz klasę, która implementuje Runnable, i przekaż wszystko, czego potrzebujesz w odpowiednio zdefiniowanym konstruktorze, lub napisz klasę, która rozszerza Thread o odpowiednio zdefiniowanym konstruktorze, który wywołuje super () z odpowiednimi parametrami.
Począwszy od Java 8, możesz użyć lambda do przechwytywania parametrów, które są faktycznie ostateczne . Na przykład:
final String param1 = "First param";
final int param2 = 2;
new Thread(() -> {
// Do whatever you want here: param1 and param2 are in-scope!
System.out.println(param1);
System.out.println(param2);
}).start();
W Javie 8 możesz używać lambda
wyrażeń z interfejsem API współbieżności i ExecutorService
jako zamiennik wyższego poziomu do bezpośredniej pracy z wątkami:
newCachedThreadPool()
Tworzy pulę wątków, która w razie potrzeby tworzy nowe wątki, ale użyje wcześniej zbudowanych wątków, gdy będą one dostępne. Pule te zazwyczaj poprawiają wydajność programów wykonujących wiele krótkotrwałych zadań asynchronicznych.
private static final ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(() -> {
myFunction(myParam1, myParam2);
});
Zobacz także executors
javadocs .
Wiem, że jestem spóźniony o kilka lat, ale natknąłem się na ten problem i podjąłem niekonwencjonalne podejście. Chciałem to zrobić bez tworzenia nowej klasy, więc oto co wymyśliłem:
int x = 0;
new Thread((new Runnable() {
int x;
public void run() {
// stuff with x and whatever else you want
}
public Runnable pass(int x) {
this.x = x;
return this;
}
}).pass(x)).start();
Przekazywanie parametrów za pomocą metod start () i run ():
// Tester
public static void main(String... args) throws Exception {
ThreadType2 t = new ThreadType2(new RunnableType2(){
public void run(Object object) {
System.out.println("Parameter="+object);
}});
t.start("the parameter");
}
// New class 1 of 2
public class ThreadType2 {
final private Thread thread;
private Object objectIn = null;
ThreadType2(final RunnableType2 runnableType2) {
thread = new Thread(new Runnable() {
public void run() {
runnableType2.run(objectIn);
}});
}
public void start(final Object object) {
this.objectIn = object;
thread.start();
}
// If you want to do things like setDaemon(true);
public Thread getThread() {
return thread;
}
}
// New class 2 of 2
public interface RunnableType2 {
public void run(Object object);
}
Możesz wyprowadzić klasę z Runnable, a podczas budowy (powiedzmy) przekaż parametr w.
Następnie uruchom go za pomocą Thread.start (Runnable r);
Jeśli masz na myśli, gdy wątek jest uruchomiony, po prostu trzymaj odwołanie do swojego obiektu pochodnego w wątku wywołującym i wywołaj odpowiednie metody ustawiające (synchronizując w razie potrzeby)
Nie, nie możesz przekazać parametrów do run()
metody. Podpis mówi ci to (nie ma parametrów). Prawdopodobnie najłatwiejszym sposobem na to byłoby użycie specjalnie zbudowanego obiektu, który pobiera parametr w konstruktorze i przechowuje go w zmiennej końcowej:
public class WorkingTask implements Runnable
{
private final Object toWorkWith;
public WorkingTask(Object workOnMe)
{
toWorkWith = workOnMe;
}
public void run()
{
//do work
}
}
//...
Thread t = new Thread(new WorkingTask(theData));
t.start();
Gdy to zrobisz - musisz uważać na integralność danych obiektu, który przekazujesz do „WorkingTask”. Dane będą teraz istnieć w dwóch różnych wątkach, więc musisz upewnić się, że są bezpieczne.
Jeszcze jedna opcja; takie podejście pozwala używać elementu Runnable jako asynchronicznego wywołania funkcji. Jeśli twoje zadanie nie musi zwracać wyniku, np. Wykonuje tylko pewną czynność, nie musisz się martwić o to, jak przekazać „wynik”.
Ten wzór pozwala ponownie użyć przedmiotu, w którym potrzebujesz pewnego rodzaju stanu wewnętrznego. Gdy parametry nie są przekazywane do konstruktora, należy zachować ostrożność, aby pośredniczyć w dostępie programów do parametrów. Możesz potrzebować więcej kontroli, jeśli twój przypadek użycia dotyczy różnych rozmówców itp.
public class MyRunnable implements Runnable
{
private final Boolean PARAMETER_LOCK = false;
private X parameter;
public MyRunnable(X parameter) {
this.parameter = parameter;
}
public void setParameter( final X newParameter ){
boolean done = false;
synchronize( PARAMETER_LOCK )
{
if( null == parameter )
{
parameter = newParameter;
done = true;
}
}
if( ! done )
{
throw new RuntimeException("MyRunnable - Parameter not cleared." );
}
}
public void clearParameter(){
synchronize( PARAMETER_LOCK )
{
parameter = null;
}
}
public void run() {
X localParameter;
synchronize( PARAMETER_LOCK )
{
localParameter = parameter;
}
if( null != localParameter )
{
clearParameter(); //-- could clear now, or later, or not at all ...
doSomeStuff( localParameter );
}
}
}
Wątek t = nowy wątek (nowy MyRunnable (parametr)); t.start ();
Jeśli potrzebujesz wyniku przetwarzania, będziesz także musiał skoordynować ukończenie MyRunnable po zakończeniu zadania cząstkowego. Możesz oddzwonić lub poczekać na Wątek „t” itp.
Dla celów oddzwaniania zwykle implementuję swój własny rodzajowy Runnable
z parametrami wejściowymi:
public interface Runnable<TResult> {
void run(TResult result);
}
Użycie jest proste:
myManager.doCallbackOperation(new Runnable<MyResult>() {
@Override
public void run(MyResult result) {
// do something with the result
}
});
W menedżerze:
public void doCallbackOperation(Runnable<MyResult> runnable) {
new AsyncTask<Void, Void, MyResult>() {
@Override
protected MyResult doInBackground(Void... params) {
// do background operation
return new MyResult(); // return resulting object
}
@Override
protected void onPostExecute(MyResult result) {
// execute runnable passing the result when operation has finished
runnable.run(result);
}
}.execute();
}
Utwórz zmienną lokalną w swojej klasie, która extends Thread
lub implements Runnable
.
public class Extractor extends Thread {
public String webpage = "";
public Extractor(String w){
webpage = w;
}
public void setWebpage(String l){
webpage = l;
}
@Override
public void run() {// l is link
System.out.println(webpage);
}
public String toString(){
return "Page: "+webpage;
}}
W ten sposób możesz przekazać zmienną po uruchomieniu.
Extractor e = new Extractor("www.google.com");
e.start();
Wyjście:
"www.google.com"