#java #multithreading
#java #многопоточность
Вопрос:
Я знаю, что есть несколько похожих вопросов, но я не смог найти конкретного ответа на этот вопрос — извиняюсь, если я был неправ, и есть. Будет ли тест.метод toString() будет выполняться основным потоком или тестовым потоком, который я запустил до его вызова? Группа из нас спорит об этом для проверки тестирования, и мне любопытно, каков ответ.
public class Main {
public static void main(String[] args) {
test = new ThreadTest("Test", 3);
test.start();
System.out.println(test.toString());
}
}
public class ThreadTest extends Thread {
public ThreadTest(String n, int x) {
setName(n);
}
@Override
public String toString() {
return(getName() ": x = " x);
}
public void run() {
//Nothing of any relevance to the problem occurs here
}
}
Комментарии:
1. Я не добавил метод run, поскольку это упрощенная версия кода, который мы обсуждаем, и он на самом деле не делает ничего особенного — метод toString() вообще не вызывается в методе run .
2. Метод класса, который расширяет поток, ничем не отличается от метода класса, который расширяет дату. Если вы вызываете с использованием объекта класса, тогда будет вызван метод.
3. Есть ли способ определить, с помощью какого потока конкретно? Будет ли это main или тестовый поток, который запускается перед его вызовом?
4. Методы вызываются в потоке, который их вызывает, даже если это метод на протекторе. toString() будет вызываться в том же потоке, что и main() . Кроме того, не переходите к подклассу Thread . Вам лучше создавать Runnables .
5. Спасибо! Это то, что я хотел проверить 🙂
Ответ №1:
toString()
Вызов выполняется в основном потоке. Основной поток вызывает Main.main()
; main()
непосредственно вызывает test.toString()
.
Просто потому, что ваш вывод выводит строку «Test», это не значит, что это поток, который его выполняет. Thread
имеет состояние; setName(...)
устанавливает это состояние (и ваш класс TestThread
является подклассом Test
, поэтому он также наследует это). В вашей toString()
реализации вы просто печатаете это состояние… не фактическое имя выполняющегося потока.
Чтобы доказать это, измените свой TestThread.toString()
метод, чтобы также печатать имя текущего выполняющегося потока и повторно запускать:
@Override
public String toString() {
return(getName() ": x = " x " executed on thread " Thread.currentThread().getName());
}
Вы увидите следующую печать для стандартного вывода:
Test: x = 3 executed on thread main
Полный код:
public class Main {
public static void main(String[] args) {
ThreadTest test = new ThreadTest("Test", 3);
test.start();
System.out.println(test.toString());
}
}
public class ThreadTest extends Thread {
private int x;
public ThreadTest(String n, int x) {
setName(n);
this.x = x;
}
@Override
public String toString() {
return(getName() ": x = " x " executed on thread " Thread.currentThread().getName());
}
public void run() {
//Nothing of any relevance to the problem occurs here
}
}
Ответ №2:
Вызовы всегда выполняются в вызывающем потоке. На этом основаны массы кода — например, вся инфраструктура безопасности и транзакций spring framework.
Этот факт можно доказать довольно легко:
public class Main {
static ThreadLocal<String> threadContext = new ThreadLocal<String>();
public static void main(String[] args) throws InterruptedException {
threadContext.set("Main");
TestThread test = new TestThread();
new Thread(test).start();
System.out.println("Main: " test.getContext());
}
static class TestThread implements Runnable {
@Override
public void run() {
threadContext.set("ThreadTest");
System.out.println("TestThread: " getContext());
}
String getContext() {
return threadContext.get();
}
}
}
И, как уже сказал @DavidEhrmann: вы должны реализовать Runnable
вместо расширения Thread
.