Допустим, есть задача: создать поток, который скачивает что-то из интернета, а из основного потока ему валятся задания. Стандартный путь реализации такой:
class DownloadThread extends Thread {
public interface Listener {
void onImageLoaded(Image image);
}
private String reqUrl_ = null;
private final Listener listener_;
private boolean isFinished_ = false;
DownloadThread(Listener listener) {
listener_ = listener;
}
public void imageRequest(String url) {
synchronized (this) {
// Запрос на загрузку изображения
reqUrl_ = url;
}
}
@Override
public void run() {
while (!isFinished_)
String url = null;
synchronized (this) {
// Проверка: есть ли запрос?
if (null != reqUrl_) {
url = reqUrl_;
reqUrl_ = null;
}
}
if (null != url) {
// Запрос есть, загружаем изображение
Image image = download(url);
listener_.onImageLoaded(image);
}
try {
// Поспим одну милисекунду, чтоб не грузить CPU
sleep(1);
}
catch (InterruptedException e) {
isFinished_ = true;
break;
}
}
}
public void stopThread() {
isFinished_ = true;
}
} |
class DownloadThread extends Thread {
public interface Listener {
void onImageLoaded(Image image);
}
private String reqUrl_ = null;
private final Listener listener_;
private boolean isFinished_ = false;
DownloadThread(Listener listener) {
listener_ = listener;
}
public void imageRequest(String url) {
synchronized (this) {
// Запрос на загрузку изображения
reqUrl_ = url;
}
}
@Override
public void run() {
while (!isFinished_)
String url = null;
synchronized (this) {
// Проверка: есть ли запрос?
if (null != reqUrl_) {
url = reqUrl_;
reqUrl_ = null;
}
}
if (null != url) {
// Запрос есть, загружаем изображение
Image image = download(url);
listener_.onImageLoaded(image);
}
try {
// Поспим одну милисекунду, чтоб не грузить CPU
sleep(1);
}
catch (InterruptedException e) {
isFinished_ = true;
break;
}
}
}
public void stopThread() {
isFinished_ = true;
}
}
Почему этот плохой, негодный пример? Потому что, когда заданий много, между ними одна миллисекунда бездействия минимум — это во-первых. Во-вторых, если задач для потока нет, то он работает без всякой пользы, создавая лишнюю нагрузку процессору.
Рассмотрим пример номер два:
class DownloadThread extends Thread {
public interface Listener {
void onImageLoaded(Image image);
}
private String reqUrl_ = null;
private final Listener listener_;
private boolean isFinished_ = false;
DownloadThread(Listener listener) {
listener_ = listener;
}
public void imageRequest(String url) {
synchronized (this) {
// Запрос на загрузку изображения
reqUrl_ = url;
// Уведомляем поток, что есть задание
notify();
}
}
@Override
public void run() {
while (!isFinished_)
String url = null;
synchronized (this) {
if (isFinished_) {
break;
}
// Проверка: есть ли запрос?
if (null != reqUrl_) {
url = reqUrl_;
reqUrl_ = null;
}
else {
try {
// Ждем вызова notify
wait();
}
catch (InterruptedException e) {
isFinished_ = true;
break;
}
}
}
if (null != url) {
// Запрос есть, загружаем изображение
Image image = download(url);
listener_.onImageLoaded(image);
}
}
}
public void stopThread() {
synchronized (this) {
isFinished_ = true;
notify();
}
}
} |
class DownloadThread extends Thread {
public interface Listener {
void onImageLoaded(Image image);
}
private String reqUrl_ = null;
private final Listener listener_;
private boolean isFinished_ = false;
DownloadThread(Listener listener) {
listener_ = listener;
}
public void imageRequest(String url) {
synchronized (this) {
// Запрос на загрузку изображения
reqUrl_ = url;
// Уведомляем поток, что есть задание
notify();
}
}
@Override
public void run() {
while (!isFinished_)
String url = null;
synchronized (this) {
if (isFinished_) {
break;
}
// Проверка: есть ли запрос?
if (null != reqUrl_) {
url = reqUrl_;
reqUrl_ = null;
}
else {
try {
// Ждем вызова notify
wait();
}
catch (InterruptedException e) {
isFinished_ = true;
break;
}
}
}
if (null != url) {
// Запрос есть, загружаем изображение
Image image = download(url);
listener_.onImageLoaded(image);
}
}
}
public void stopThread() {
synchronized (this) {
isFinished_ = true;
notify();
}
}
}
Почему этот пример лучше первого? Во-первых, когда заданий нет, поток находится в режиме ожидания без вызовов sleep()
. Во-вторых, когда задание приходит, поток реагирует моментально просыпаясь.
Обратите внимание, что метод stopThread()
слегка изменился. Ввиду того, что поток не выйдет из состояния wait()
без вызова notify()
.
UPD: По совету друзей исправил метод run()
для корректного завершения потока.
В обоих примерах не учитывается, что изображения нам нужны пачкой сразу, то есть очередь запросов выкинута для упрощения кода.
P.S. Кстати, в C/C++ для тех же целей можно использовать pthread_cond_wait()
и pthread_cond_signal()