Хто-небудь може підказати мені, як я можу передати параметр до потоку?
Також, як це працює для анонімних занять?
Consumer<T>
.
Хто-небудь може підказати мені, як я можу передати параметр до потоку?
Також, як це працює для анонімних занять?
Consumer<T>
.
Відповіді:
Вам потрібно передати параметр у конструкторі об’єкту Runnable:
public class MyRunnable implements Runnable {
public MyRunnable(Object parameter) {
// store parameter for later user
}
public void run() {
}
}
і викликати це таким чином:
Runnable r = new MyRunnable(param_value);
new Thread(r).start();
r
буде мати один і той же аргумент, тому, якщо ми хочемо передати різні аргументи декільком запущеним потокам, MyThread
нам знадобиться створити новий MyThread
екземпляр, використовуючи потрібний аргумент для кожної теми. Іншими словами, для початку роботи потоку нам потрібно створити два об’єкти: Thread та MyThread. Це вважається поганим та ефективним?
У відповідь на редагування питань ось як це працює для занять Anonymous
final X parameter = ...; // the final is important
Thread t = new Thread(new Runnable() {
p = parameter;
public void run() {
...
};
t.start();
У вас є клас, який розширює Thread (або реалізує Runnable) та конструктор з параметрами, які ви хочете передати. Потім, створюючи новий потік, вам потрібно передати аргументи, а потім запустити нитку, приблизно так:
Thread t = new MyThread(args...);
t.start();
Runnable - це набагато краще рішення, ніж Thread BTW. Тому я вважаю за краще:
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();
Ця відповідь в основному така ж, як і подібне запитання: Як передавати параметри об’єкту Thread
parameter
безпосередньо з run()
методу, не використовуючи поле, як p
взагалі. Здається, працює. Чи є якась тонка багатопотокова річ, яку я пропускаю, не попередньо копіюючи parameter
її p
?
)
у першому прикладі
final X parameter
раніше new Runnable()
лінію, то я міг отримати доступ parameter
всередину run()
. Мені не потрібно було робити зайвого p = parameter
.
final
вже не так важливо; цього достатньо, якщо змінна буде ефективно остаточною (незважаючи на те, що вона не шкодить, якщо вона є)
через конструктор класу Runnable або 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
?
@Override
прямо заявляє, що це перекриває абстрактний метод у класі Thread.
Ця відповідь приходить дуже пізно, але, можливо, хтось знайде її корисною. Йдеться про те, як передати параметр (и) до Runnable
без навіть оголошення декларованого імені класу (зручно для інлайнерів):
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();
Звичайно, ви можете відкласти виконання start
на якийсь більш зручний або відповідний момент. І саме від вас залежить, яким буде підписinit
методу (так що це може знадобитися більше та / або різних аргументів) і звичайно навіть його назва, але в основному ви отримуєте уявлення.
Насправді існує також інший спосіб передачі параметра анонімному класу з використанням ініціалізаційних блоків. Врахуйте це:
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();
Так все відбувається всередині блоку ініціалізатора.
this.myParam
дійсно тоді потрібне? Не могли ви просто скинути приватні змінні та звернутися до змінної із зовнішньої області застосування? Я розумію (звичайно), що це має певні наслідки, такі як змінна, відкрита для зміни після запуску потоку.
Під час створення потоку вам потрібен екземпляр Runnable
. Найпростіший спосіб передати параметр - передати його як аргумент конструктору:
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();
Якщо ви хочете змінити параметр під час запуску потоку, ви можете просто додати метод встановлення у свій клас запуску:
public void setMyParam(String value){
this.myParam = value;
}
Після цього ви можете змінити значення параметра, зателефонувавши так:
myRunnable.setMyParam("Goodbye World");
Звичайно, якщо ви хочете запустити дію при зміні параметра, вам доведеться використовувати блокування, що робить речі значно складнішими.
Для створення потоку ви зазвичай створюєте власну реалізацію Runnable. Передайте параметри потоку в конструкторі цього класу.
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);
}
}
Ви можете або розширити або, і надати параметри за своїм бажанням. Є прості приклади в документації . Я відправлю їх сюди: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();
Або напишіть клас, який реалізує Runnable, і передайте все, що вам потрібно, у відповідно визначений конструктор, або напишіть клас, який розширює Thread з відповідно визначеним конструктором, який викликає super () з відповідними параметрами.
Що стосується Java 8, ви можете використовувати лямбда для збору параметрів, які є фактично остаточними . Наприклад:
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();
У Java 8 ви можете використовувати lambda
вирази з API Concurrency & the ExecutorService
як заміну вищого рівня для роботи з потоками безпосередньо:
newCachedThreadPool()
Створює пул потоків, який створює нові потоки за потребою, але повторно використовує попередньо побудовані нитки, коли вони доступні. Ці пули, як правило, покращують продуктивність програм, які виконують багато короткочасних асинхронних завдань.
private static final ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(() -> {
myFunction(myParam1, myParam2);
});
Дивіться також executors
javadocs .
Я знаю, що я запізнююсь на кілька років, але я натрапив на це питання і застосував неортодоксальний підхід. Я хотів це зробити, не створюючи нового класу, тому ось що я придумав:
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();
Параметр, що проходить методами start () та 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);
}
Ви можете отримати клас із Runnable, а під час побудови (скажімо) передайте параметр в.
Потім запустіть його за допомогою Thread.start (Runnable r);
Якщо ви маєте на увазі, поки нитка запущена, просто утримуйте посилання на свій похідний об'єкт у викличній потоці та зателефонуйте у відповідні методи встановлення (синхронізуючи, де це можливо)
Ні, ви не можете передавати параметри run()
методу. Підпис говорить вам про те, що (у нього немає параметрів). Напевно, найпростішим способом це було б використовувати об'єкт, побудований за призначенням, який приймає параметр у конструкторі та зберігає його у кінцевій змінній:
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();
Як тільки ви це зробите - ви повинні бути обережними щодо цілісності даних об'єкта, який ви передаєте у "WorkingTask". Дані тепер існуватимуть у двох різних потоках, тому ви повинні переконатися, що це безпечно для потоків.
Ще один варіант; такий підхід дозволяє використовувати елемент Runnable, як асинхронний виклик функції. Якщо ваше завдання не потребує повернення результату, наприклад, воно виконує певну дію, вам не потрібно турбуватися про те, як ви повернете "результат".
Цей зразок дозволяє повторно використовувати предмет, де вам потрібен якийсь внутрішній стан. Якщо не пропускати параметри (и) в конструкторі, необхідно доглядати за програмами доступу до параметрів. Можливо, вам знадобиться більше перевірок, якщо у вашому випадку використання залучаються різні абоненти тощо.
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 );
}
}
}
Thread t = new Thread (новий MyRunnable (параметр)); t.start ();
Якщо вам потрібен результат обробки, вам також потрібно буде координувати завершення MyRunnable, коли підзадача закінчиться. Ви можете передати дзвінок назад або просто зачекати на "T" нитки тощо.
Для цілей зворотного дзвінка я зазвичай реалізую власне загальне Runnable
із вхідними параметрами:
public interface Runnable<TResult> {
void run(TResult result);
}
Використання просте:
myManager.doCallbackOperation(new Runnable<MyResult>() {
@Override
public void run(MyResult result) {
// do something with the result
}
});
У менеджері:
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();
}
Створіть у своєму класі локальну змінну, яка extends Thread
або 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;
}}
Таким чином, ви можете передавати змінну під час її запуску.
Extractor e = new Extractor("www.google.com");
e.start();
Вихід:
"www.google.com"