Понимание потоков, реализующих Runnable на java

#java #multithreading #runnable

#java #многопоточность #runnable

Вопрос:

Я пытаюсь понять потоки на Java. Я создал поток, реализующий Runnable интерфейс, который выполняет определенную функцию определенного класса. Ниже приведены классы Java:

MyThread Class

 public class MyThread implements Runnable {
    private DataContainer dataContainer;
    private Thread thread;
    public MyThread(DataContainer dataContainer) {
        this.dataContainer = dataContainer;
        thread = new Thread(this);
        thread.start();
        thread.setName("Thread 2");
    }
    @Override
    public void run() {
        System.out.println("MyThread : " dataContainer.toString());
    }
    public void runThread() {
        thread.run();
    }
}
  

DataContainer Class . Функция, которую я выполняю в потоке

 public class DataContainer {
    private static DataContainer instance = null;
    private DataContainer() {}
    public static DataContainer getInstance() {
        if(null == instance)
            instance = new DataContainer();
        return instance;
    }
    @Override
    public String toString() {
        return Thread.currentThread().getName() " : __Data__";
    }
}
  

The main class

 public class Launcher {
    public static void main(String[] args) {
        DataContainer dataContainer = DataContainer.getInstance();
        MyThread myThread = new MyThread(dataContainer);

        int i =0;
        while(i  <10) {
            System.out.println("Launcher : "  dataContainer.toString());
            myThread.runThread();
        }
    }
}
  

Проблема в while цикле, в котором выполняется DataContainer.toString() из основного класса и экземпляра MyThread я получаю выходные данные, поскольку все они выполняются в одном потоке: main thread . Ни одно из исполнений в цикле не выполняется из Thread 2 .

Моя цель состоит в том, in the while loop, I want myThread.runThread() чтобы выполняться в потоке Thread 2 .

Редактировать:

Добавлено thread.start() . Однако теперь он выполняется только один раз? Я не могу создавать новый поток каждый раз, когда хочу выполнить MyThread.runThread(). Создавая новый поток в цикле, я бы в конечном итоге создал кучу потоков. Целесообразно ли это? Я хочу, чтобы определенный фрагмент кода (скажем, Y) выполнялся потоком (скажем, X). Каждый раз, когда Y должен быть выполнен, это должно выполняться через поток X.

Решение

Это может быть возможным решением:

 public class MyThread implements Runnable {
    private DataContainer dataContainer;
    private Thread thread = null;
    public MyThread(DataContainer dataContainer) {
        this.dataContainer = dataContainer;
    }
    @Override
    public void run() {
        System.out.println("MyThread : " dataContainer.toString());
    }
    public void runThread() {
        boolean threadIsAlive = (null!=thread amp;amp; Thread.getAllStackTraces().keySet().contains(thread));
        if(null == thread || !threadIsAlive) {
            thread = new Thread(this);
            thread.start();
        } else {
            thread.run();
        }
    }
}
  

Комментарии:

1. Как запустить поток?

2. вы должны выполнить myThread.start(); , чтобы запустить его в другом потоке

3. Забыл добавить этот код. Отредактировал мой вопрос. Я думаю, что это решает мой вопрос

4. myThread.runThread() просто вызовет этот метод в текущем потоке. Если вы хотите, чтобы это было выполнено в новом потоке, поместите этот вызов в run метод и просто вызовите thread.start() . это запускает другой поток и вызывает метод run вашего runnable объекта.

5. Если вам требуется создать новый поток в while цикле, пожалуйста, поместите инструкции Thread creating в runThread метод

Ответ №1:

thread.run(); просто вызывает ваш Runnable s run метод напрямую, в контексте текущего потока. Вы получите тот же результат, если вас просто вызовут run() в вашем runThread методе.

Вам нужно вызвать thread.start() , это создаст новый поток процесса и в конечном итоге вызовет ваш Runnable s run метод

Вместо того, чтобы Thread вместо a Runnable оборачивать a, что противоречит здравому смыслу и, как правило, плохой дизайн, вы должны создать Thread и установить Runnable для него, когда вам нужно, например…

 public class MyThread implements Runnable {
    private DataContainer dataContainer;
    public MyThread(DataContainer dataContainer) {
        this.dataContainer = dataContainer;
    }
    @Override
    public void run() {
        System.out.println("MyThread : " dataContainer.toString());
    }
}
  

Тогда вы могли бы сделать что-то вроде…

 DataContainer dataContainer = DataContainer.getInstance();
MyThread myThread = new MyThread(dataContainer);

Thread thread = new Thread(myThread );
thread.setName("Thread 2");
thread.start();

System.out.println("Launcher : "   dataContainer.toString());
  

Ответ №2:

myThread.runThread() просто вызовет этот метод в текущем потоке. Если вы хотите, чтобы это было выполнено в новом потоке, поместите этот вызов в run метод и просто вызовите myThread.start() . Это запускает другой поток и вызывает метод run вашего runnable объекта.

Ответ №3:

Когда вы определяете класс, который реализует Runnable, вам не нужно иметь в нем объект Thread.

Упрощенно

  class myThread implements Runnable {

     @Override
    public void run() {
          // do something
   }
}


new Thread (new myThread ()).start ();
  

Ответ №4:

Я не пытался понять ваш пример (слишком запутанный), но это плохая идея:

 public class Foobar implements Runnable {
    private XType x;
    private YType y;
    private Thread thread;

    public Foobar() {
        x = new XType();
        thread = new Thread(this);   //Not in a constructor!!!
        thread.start();
        y = new YType();
    }

    @Override
    public void run() {
        doSomethingWith(y);       // y can be uninitialized at this point.
        doSomethingElseWith(x);   // x can be uninitialized too!!!
    }
}
  

У этого антишаблона есть имя, оно называется «утечка this из конструктора». Конструктор Foobar запускает новый поток и дает ему ссылку на новый экземпляр Foobar (т.Е. this ) до того, как новый объект был полностью инициализирован. Спецификация языка Java не дает никаких гарантий относительно того, как объект будет выглядеть в новом потоке, когда объект будет опубликован таким образом. (Google для «безопасной публикации»)

В частности, при первом вводе метода run() любая из переменных-членов объекта (включая x!!) может показаться неинициализированной с точки зрения нового потока.

Лучшая практика: никогда не используйте this в конструкторе или в любом вспомогательном методе, который вызывается конструктором, а также объявляйте помощников равными final , чтобы подкласс не мог переопределить один и таким образом увидеть неинициализированный объект.

 class Foobar {

    Foobar(...) {
        ...don't use "this" here...
        fooHelper(...);
    }

    private final void fooHelper(...) {
        ...don't use "this" here either...
    }
}