Если метод принадлежит другому классу, который расширяет поток, но вызывается из основного потока, будет ли он выполняться основным или дочерним потоком? (Java)

#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 .