#java
Вопрос:
Я хотел бы задать основной вопрос о потоках Java. Давайте рассмотрим сценарий «производитель — потребитель». Допустим, есть один производитель и n потребителей. Потребители прибывают в произвольное время, и как только они обслуживаются, они уходят, то есть каждый потребитель работает в своем собственном потоке. Должен ли я по-прежнему использовать условие run forever для потребителя ?
public class Consumer extends Thread {
public void run() {
while (true) {
}
}
}
Разве это не будет поддерживать поток вечно ?
Комментарии:
1. Не уверен, что понимаю ваш вопрос. Эта нить действительно будет работать вечно. Следует ли использовать условие «запуск навсегда» или нет, полностью зависит от вас.
Ответ №1:
Я бы не стал расширять поток, вместо этого я бы реализовал возможность запуска.
Если вы хотите, чтобы поток работал вечно, я бы сделал так, чтобы он работал вечно.
Распространенной альтернативой является использование
while(!Thread.currentThread().isInterrupted()) {
или
while(!Thread.interrupted()) {
Комментарии:
1. Дает ли это гарантию того, что другие потоки (потребители, которые не обслуживаются) не умрут рано ?
2. То, что одна нить останавливает другую, — это не то, что происходит просто так. На самом деле это то, что не так просто сделать. Можете ли вы объяснить, почему вас беспокоит, что другие потоки могут умереть?
3. Рад, что ты спросил. Я новичок в Яве и ОП, если на то пошло. Допустим, в любой момент времени есть 5 потребителей, и они ждут, чтобы их обслужил производитель. Таким образом, очевидно, что будет 5 потоков потребителей. Поскольку Thread.interrupted() является статическим методом, если один поток прерывается, не приведет ли это к тому, что все другие потоки также погибнут ?
4. Каждый поток имеет свой собственный флаг прерывания. Не существует простого способа прервать каждый поток.
Ответ №2:
Так и будет, так что вы, возможно, захотите сделать что-то вроде
while(beingServed)
{
//check if the customer is done being served (set beingServed to false)
}
Таким образом, вы избежите петли, когда ей суждено умереть.
Ответ №3:
Почему бы не использовать boolean
символ, который отражает присутствие Потребителя?
public class Consumer extends Thread {
private volatile boolean present;
public Consumer() {
present = true;
}
public void run() {
while (present) {
// Do Stuff
}
}
public void consumerLeft() {
present = false;
}
}
Комментарии:
1. настоящее должно быть изменчивым. 😉
2. @PeterLawrey Я знал, что кое-что забыл 😛
Ответ №4:
Во-первых, вы можете создать для каждого потребителя, и после того, как потребитель закончит свою работу, он завершит функцию запуска и умрет, поэтому нет необходимости в бесконечном цикле. однако создание потока для каждого потребителя не является хорошей идеей, так как создание потока довольно дорого с точки зрения производительности. потоки-это очень дорогие ресурсы. Кроме того, я согласен с приведенными выше ответами о том, что лучше реализовать запускаемый, а не расширять поток. расширяйте поток только тогда, когда вы хотите настроить свой поток. Я настоятельно рекомендую вам использовать пул потоков, и потребителем будет управляемый объект, запущенный потоком в пуле потоков. код должен выглядеть следующим образом:
public class ConsumerMgr{
int poolSize = 2;
int maxPoolSize = 2;
long keepAliveTime = 10;
ThreadPoolExecutor threadPool = null;
final ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(
5);
public ConsumerMgr()
{
threadPool = new ThreadPoolExecutor(poolSize, maxPoolSize,
keepAliveTime, TimeUnit.SECONDS, queue);
}
public void runTask(Runnable task)
{
// System.out.println("Task count.." threadPool.getTaskCount() );
// System.out.println("Queue Size before assigning the
// task.." queue.size() );
threadPool.execute(task);
// System.out.println("Queue Size after assigning the
// task.." queue.size() );
// System.out.println("Pool Size after assigning the
// task.." threadPool.getActiveCount() );
// System.out.println("Task count.." threadPool.getTaskCount() );
System.out.println("Task count.." queue.size());
}
Ответ №5:
Не стоит расширять поток (если только вы не кодируете новый вид потока — т. Е. Никогда).
Лучший подход-передать a Runnable
конструктору потока, как это:
public class Consumer implements Runnable {
public void run() {
while (true) {
// Do something
}
}
}
new Thread(new Consumer()).start();
В общем, while(true)
все в порядке, но вам придется смириться с тем, что вас прервали, либо обычным wake
, либо ложным пробуждением. В Интернете есть много примеров.
Я рекомендую читать параллелизм Java на практике.
Комментарии:
1. Что такое пробуждение? И как вы вызываете ложное пробуждение? Разве ложное пробуждение по определению не является пробуждением, которое не было вызвано кодом?
Ответ №6:
для шаблона производитель-потребитель вам лучше использовать wait() и notify(). Смотрите этот учебник. Это гораздо эффективнее, чем использование цикла while(true).
Комментарии:
1. Было бы еще лучше и проще использовать блокирующую очередь.
Ответ №7:
Если вы хотите, чтобы ваш поток обрабатывал сообщения до тех пор, пока вы их не убьете (или они каким-то образом будут убиты), внутри while (true)
будет какой-то синхронизированный вызов вашему потоку-производителю (или синхронизированной очереди или системе очередей), который будет блокироваться до тех пор, пока сообщение не станет доступным. Как только сообщение израсходовано, цикл перезапускается и снова ожидает.
Если вы хотите вручную создать экземпляр группы потоков, которые получают сообщение от производителя только один раз, а затем умирают, не используйте while (true)
.