Java многопоточный пример - объект является общим или нет - PullRequest
0 голосов
/ 25 февраля 2020

У меня есть вопрос по поводу следующего примера. У нас есть основной класс, который должен загружать много файлов с веб-сервера и DownloadTask, который выполняет эту работу. В Main.java есть объект saveTo, который перезаписывается каждые l oop. В этом объекте l oop передается DownloadTask в качестве ссылки в конструкторе. DownloadTask берет этот объект и выполняет с ним некоторую работу.

  1. Может ли объект saveTo быть перезаписан для l oop в основном методе, прежде чем он может быть обработан DownloadTask?

    1.1 Почему можно / почему не можешь. Как работает создание объектов в l oop?

  2. Имеет ли это значение, если это:

...
for (File fileToDownload: filesToDownload ) {
                File saveTo = new File("C:\\temp\\"+fileToDownload.getName());
                ...

изменится на это :

...
File saveTo;
for (File fileToDownload: filesToDownload ) {
                saveTo = new File("C:\\temp\\"+fileToDownload.getName());
                ...

Main. java:

public static void main(String[] args) {
            ArrayList<File> filesToDownload = new ArrayList<>();
            filesToDownload.add(new File("File1"));
            filesToDownload.add(new File("File2"));
            filesToDownload.add(new File("File3"));
            ...
            filesToDownload.add(new File("File100"));

            ExecutorService pool = Executors.newFixedThreadPool(2);
            CompletionService<Boolean> completionService = new ExecutorCompletionService<>(pool);
            ArrayList<Future<Boolean>> futures = new ArrayList<>();

            for (File fileToDownload: filesToDownload ) {
                File saveTo = new File("C:\\temp\\"+fileToDownload.getName());
                Future<Boolean> f = completionService.submit(new DownloadTask(new URL("http://1.1.1.1/" + fileToDownload.getName()), saveTo));
                futures.add(f);
            }

            for (int tasks=0;tasks<futures.size();tasks++) {
                completionService.take();
            }        
}

И это DownloadTask. java:

public class DownloadTask implements Callable<Boolean> {

                private URL fileURL;
                private File saveTo;

                public DownloadTask(URL fileURL, File saveTo) {
                    this.fileURL = fileURL;
                    this.saveTo = saveTo;
                }

                private void downloadFile(URL fileURL, File saveTo) throws IOException {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    ReadableByteChannel readableByteChannel = Channels.newChannel(fileURL.openStream());

                    FileOutputStream fileOutputStream = new FileOutputStream(saveTo);
                    fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE);

                    fileOutputStream.close();
                    readableByteChannel.close();
                }

                @Override
                public Boolean call() throws IOException {
                    downloadFile(fileURL, saveTo);
                    return true;
                }
            }

Спасибо!

Ответы [ 2 ]

2 голосов
/ 25 февраля 2020

Как работает создание объектов в l oop?

Это работает точно так же, как и где-либо еще. Каждая оценка выражения new File(...) вызывает выделение нового экземпляра класса File из кучи, вызывает вызов конструктора с аргументом ... и возвращает ссылку на новый File instance.

Ваш пример кода создает новый экземпляр File на каждой итерации l oop и сохраняет ссылку на него в локальной переменной saveTo, перезаписывая предыдущее значение saveTo , Это присвоение saveTo не влияет на новый экземпляр File, а не влияет на экземпляр File, к которому saveTo ранее порекомендовано.

Единственное место, где используется saveTo, находится на следующей строке:

...f = completionService.submit(new DownloadTask(..., saveTo));

Таким образом, единственный эффект присвоения saveTo - определить, какое File экземпляр будет предоставляться каждому новому DownloadTask, который создает ваша программа.

Имеет ли значение, если [объявление saveTo перемещено из l oop?]

Это не будет иметь никакого значения для как работает ваша программа . Компилятор выдаст один и тот же код в любом случае. Это может повлиять на то, насколько легко другим программистам будет читать ваш код. Когда кто-то видит, что saveTo объявлено внутри тела for l oop, ему не нужно тратить время на поиски, где еще может быть использовано. Если вы объявите это внутри l oop, то Java не позволит вам использовать его за пределами l oop.

1 голос
/ 25 февраля 2020

Ваш код является поточно-ориентированным (другими словами, это нормально). Проблема с безопасностью потоков может возникнуть в следующем примере:

public class MyClass {
  private int counter = 0;

  public int myMethod() {
    ...
    counter++;
    ...
    return counter;
  }
}

Представьте, что вы создаете один экземпляр MyClass и позволяете некоторому коду в разных потоках одновременно вызывать myMethod(). Учтите, что 2 потока запускают его. оба увеличивают ваш счетчик (который является общим) на единицу перед каждым возвращением. Каждый ожидает возврата 1, но будет 2. В вашем случае нет никаких ресурсов вне методов. Так что разные темы не мешают друг другу. Чтобы исправить приведенный выше пример, вам нужно изменить его на следующее:

  public class MyClass {

  public int myMethod(int counter) {
    ...
    counter++;
    ...
    return counter;
  }
}

В этом случае в каждом потоке будет отдельный экземпляр counter - not shared. И все будет работать как положено. Таким образом, потокобезопасен. Обратите внимание, что это очень простой пример c, а безопасность потоков - очень сложная и непростая проблема.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...