В моем приложении есть 2 фазы, один загружает некоторые большие данные, а другой манипулирует им. поэтому я создал 2 классы, которые реализуют исполняемым: ImageDownloader и ImageManipulator, и они разделяют downloadedBlockingQueue:Java Producer Consumer ArrayBlockingQueue deadlock on take()
public class ImageDownloader implements Runnable {
private ArrayBlockingQueue<ImageBean> downloadedImagesBlockingQueue;
private ArrayBlockingQueue<String> imgUrlsBlockingQueue;
public ImageDownloader(ArrayBlockingQueue<String> imgUrlsBlockingQueue, ArrayBlockingQueue<ImageBean> downloadedImagesBlockingQueue) {
this.downloadedImagesBlockingQueue = downloadedImagesBlockingQueue;
this.imgUrlsBlockingQueue = imgUrlsBlockingQueue;
}
@Override
public void run() {
while (!this.imgUrlsBlockingQueue.isEmpty()) {
try {
String imgUrl = this.imgUrlsBlockingQueue.take();
ImageBean imageBean = doYourThing(imgUrl);
this.downloadedImagesBlockingQueue.add(imageBean);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ImageManipulator implements Runnable {
private ArrayBlockingQueue<ImageBean> downloadedImagesBlockingQueue;
private AtomicInteger capacity;
public ImageManipulator(ArrayBlockingQueue<ImageBean> downloadedImagesBlockingQueue,
AtomicInteger capacity) {
this.downloadedImagesBlockingQueue = downloadedImagesBlockingQueue;
this.capacity = capacity;
}
@Override
public void run() {
while (capacity.get() > 0) {
try {
ImageBean imageBean = downloadedImagesBlockingQueue.take(); // <- HERE I GET THE DEADLOCK
capacity.decrementAndGet();
} catch (InterruptedException e) {
e.printStackTrace();
}
// ....
}
}
}
public class Main {
public static void main(String[] args) {
String[] imageUrls = new String[]{"url1", "url2"};
int capacity = imageUrls.length;
ArrayBlockingQueue<String> imgUrlsBlockingQueue = initImgUrlsBlockingQueue(imageUrls, capacity);
ArrayBlockingQueue<ImageBean> downloadedImagesBlockingQueue = new ArrayBlockingQueue<>(capacity);
ExecutorService downloaderExecutor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
Runnable worker = new ImageDownloader(imgUrlsBlockingQueue, downloadedImagesBlockingQueue);
downloaderExecutor.execute(worker);
}
downloaderExecutor.shutdown();
ExecutorService manipulatorExecutor = Executors.newFixedThreadPool(3);
AtomicInteger manipulatorCapacity = new AtomicInteger(capacity);
for (int i = 0; i < 3; i++) {
Runnable worker = new ImageManipulator(downloadedImagesBlockingQueue, manipulatorCapacity);
manipulatorExecutor.execute(worker);
}
manipulatorExecutor.shutdown();
while (!downloaderExecutor.isTerminated() && !manipulatorExecutor.isTerminated()) {
}
}
}
Тупик происходит потому, что этот сценарий: t1 проверяет способность его 1.
t2 проверяет 1.
t3 проверяет его 1.
t2 принимает, устанавливает мощность 0, продолжить с потоком и в конце концов выходит. t1 и t3 теперь находятся в тупике, потому что не будет добавлено к загружаемомуImagesBlockingQueue.
В конце концов я хочу что-то вроде этого: когда емкость достигнута & & очередь пуста = сломать «while» и закончить изящно.
, чтобы установить «очередь пуста», поскольку только условие не будет работать, в начале пуста оно будет пустым, пока какой-нибудь ImageDownloader не поместит imageBean в очередь.
Нет BlockingQueue может иметь нулевые элементы: https://docs.oracle.com/javase/8/docs/api/java /util/concurrent/BlockingQueue.html#add-E- –
Это всего лишь пример. Как я уже сказал, элемент ** маркер **. Вы можете объявить статическое конечное поле «статический конечный маркер ImageBean = новый ImageBean()» и использовать его как маркер и сравнивать, используя сравнение идентичности 'while ((imageBean = скачаноImagesBlockingQueue.take())!)) –