Как вызывать один и тот же thread несколько раз?

Я получаю список фото из ВК в виде url. Потом по каждому url получаю сами фотки. Делаю это, естественно, в отдельном потоке:

final int finalI = i;
Thread thread = new Thread(() -> {
    try {
        synchronized (this) {
            arrayListParticipants.get(finalI).setAvatarUser(
                    BitmapFactory.decodeStream((InputStream) new URL(tempURLs.get(finalI)).getContent()));

       
      Objects.requireNonNull(getActivity()).runOnUiThread(adapterParticipants::notifyDataSetChanged);
        }
    }
    catch (Exception e) { e.printStackTrace(); }
});

thread.setDaemon(true);
thread.start();

Всё прекрасно загружается, но в логах я вижу это:

W/grams.managerv: Long monitor contention with owner Thread-436 (3651) at void com.toprograms.managervk.ParticipantsGroup$1.lambda$onComplete$0$ParticipantsGroup$1(int)(ParticipantsGroup.java:113) waiters=60 in void com.toprograms.managervk.ParticipantsGroup$1.lambda$onComplete$0$ParticipantsGroup$1(int) for 12.026s
    Current owner stack:
        at com.toprograms.managervk.ParticipantsGroup$1.lambda$onComplete$0$ParticipantsGroup$1(ParticipantsGroup.java:113)
      - waiting to lock <0x0bb7db7b> (a com.toprograms.managervk.ParticipantsGroup$1) held by thread 378
        at com.toprograms.managervk.-$$Lambda$ParticipantsGroup$1$rl4kQMqWEgy_4KiHiS9d9kFwpps.run(lambda:-1)
        at java.lang.Thread.run(Thread.java:764)
    Contender stack:
        at com.toprograms.managervk.ParticipantsGroup$1.lambda$onComplete$0$ParticipantsGroup$1(ParticipantsGroup.java:113)
        at com.toprograms.managervk.-$$Lambda$ParticipantsGroup$1$rl4kQMqWEgy_4KiHiS9d9kFwpps.run(lambda:-1)
        at java.lang.Thread.run(Thread.java:764)

Как я понимаю, я кучу раз создаю кучу потоков. И это нехорошо. Более того, когда ухожу из этого фрагмента, а потом возвращаюсь, получаю OutOfMemoryError при попытке thread.start():

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.toprograms.managervk, PID: 2768
    java.lang.OutOfMemoryError: Could not allocate JNI Env: Failed anonymous mmap(0x0, 8192, 0x3, 0x22, -1, 0): Operation not permitted. See process maps in the log.

Вопрос: как я могу создать один поток, но так, чтобы он вызывался каждый раз, когда у меня меняется url? Или может есть другие варианты реализации такой задачи? Может быть RXJava? Но тут я совсем новичок)


Ответы (3 шт):

Автор решения: Eugene Krivenja

Блок synchronized здесь вообще лишний.

В потоке обычным способом читайте и декодируйте Bitmap.
Как он будет готов, вызывайте runOnUiThread() с setAvatarUser() и notifyDataSetChanged() внутри.

Как эффективно загружать Bitmaps хорошо описано здесь со ссылками на рекомендуемые библиотеки.
https://developer.android.com/topic/performance/graphics/load-bitmap

→ Ссылка
Автор решения: Виктор Шамрук

Во-первых, всё-таки Picasso лучше всех) Используя его, получил быстрее загрузку картинок. Во-вторых, Picasso тоже нужен UI) НО! Нашёл решение-обход (взял отсюда):

int finalI = i;

Picasso.with(getContext()).load(tempURLs.get(finalI)).into(new Target() {

@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
    arrayListParticipants.get(finalI).setAvatarUser(bitmap);
    adapterParticipants.notifyDataSetChanged();
}

@Override
public void onBitmapFailed(Drawable errorDrawable) { }

@Override
public void onPrepareLoad(Drawable placeHolderDrawable) { }

});

В-третьих, крутил вертел как только мог synchronized, но либо всё зависало, пока не прогрузятся все картинки, либо работало также с OutMemory. Как-то так. Возможно кому-то это тоже поможет.

→ Ссылка
Автор решения: Alexander Pavlov

Потоки не стоит создавать направо и налево, это дорогой ресурс. В подобных ситуация стоит использовать thread pool.

ForkJoinPool.commonPool().execute(() -> {
  try {
    arrayListParticipants
      .get(finalI)
      .setAvatarUser(
       BitmapFactory.decodeStream((InputStream) new URL(tempURLs.get(finalI)).getContent())
    );
  
    Objects.requireNonNull(getActivity())
      .runOnUiThread(adapterParticipants::notifyDataSetChanged);
  } catch (Exception e) { 
    e.printStackTrace(); 
  }
});

Thread pool не создаёт новые потоки на каждую задачу, а ставит в очередь, если все потоки заняты.

→ Ссылка