Posso obter um cenário simples e completo, isto é, um tutorial que sugere como isso deve ser usado, especificamente com uma Fila?
Os métodos wait()
e notify()
são projetados para fornecer um mecanismo para permitir que um encadeamento seja bloqueado até que uma condição específica seja atendida. Para isso, suponho que você queira escrever uma implementação de fila de bloqueio, na qual você tenha algum armazenamento fixo de elementos de tamanho fixo.
A primeira coisa que você precisa fazer é identificar as condições que você deseja que os métodos esperem. Nesse caso, você desejará que o put()
método seja bloqueado até que haja espaço livre na loja e que você deseje que o take()
método seja bloqueado até que haja algum elemento para retornar.
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public synchronized void put(T element) throws InterruptedException {
while(queue.size() == capacity) {
wait();
}
queue.add(element);
notify(); // notifyAll() for multiple producer/consumer threads
}
public synchronized T take() throws InterruptedException {
while(queue.isEmpty()) {
wait();
}
T item = queue.remove();
notify(); // notifyAll() for multiple producer/consumer threads
return item;
}
}
Há algumas coisas a serem observadas sobre a maneira pela qual você deve usar os mecanismos de espera e notificação.
Primeiro, você precisa garantir que todas as chamadas para wait()
ou notify()
estejam dentro de uma região sincronizada do código (com as chamadas wait()
e notify()
sendo sincronizadas no mesmo objeto). A razão para isso (além das preocupações de segurança de linha padrão) deve-se a algo conhecido como sinal perdido.
Um exemplo disso é que um encadeamento pode chamar put()
quando a fila estiver cheia e, em seguida, verifica a condição, vê que a fila está cheia, mas antes que possa bloquear outro encadeamento está agendado. Esse segundo encadeamento take()
é um elemento da fila e notifica os encadeamentos em espera de que a fila não está mais cheia. No entanto, como o primeiro encadeamento já verificou a condição, ele simplesmente chamará wait()
após ser remarcado, mesmo que possa progredir.
Ao sincronizar em um objeto compartilhado, você pode garantir que esse problema não ocorra, pois a chamada do segundo encadeamento take()
não poderá progredir até que o primeiro encadeamento seja realmente bloqueado.
Em segundo lugar, você precisa colocar a condição que está verificando em um loop while, em vez de uma instrução if, devido a um problema conhecido como despertares espúrios. É aqui que um segmento em espera às vezes pode ser reativado sem notify()
ser chamado. Colocar essa verificação em um loop while garantirá que, se ocorrer uma ativação espúria, a condição será verificada novamente e o thread chamará wait()
novamente.
Como algumas das outras respostas mencionaram, o Java 1.5 introduziu uma nova biblioteca de simultaneidade (no java.util.concurrent
pacote) que foi projetada para fornecer uma abstração de nível mais alto através do mecanismo de espera / notificação. Usando esses novos recursos, você pode reescrever o exemplo original da seguinte maneira:
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public void put(T element) throws InterruptedException {
lock.lock();
try {
while(queue.size() == capacity) {
notFull.await();
}
queue.add(element);
notEmpty.signal();
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
while(queue.isEmpty()) {
notEmpty.await();
}
T item = queue.remove();
notFull.signal();
return item;
} finally {
lock.unlock();
}
}
}
Obviamente, se você realmente precisa de uma fila de bloqueio, use uma implementação da interface BlockingQueue .
Além disso, para coisas como essa, eu recomendo o Java Concurrency in Practice , pois abrange tudo o que você poderia querer saber sobre problemas e soluções relacionados à concorrência.
notify
acorda apenas um segmento. Se dois segmentos do consumidor estiverem competindo para remover um elemento, uma notificação poderá ativar o outro segmento do consumidor, que não pode fazer nada a respeito e voltará a dormir (em vez do produtor, que esperávamos que fosse inserir um novo elemento). o encadeamento do produtor não é ativado, nada é inserido e agora os três encadeamentos ficam suspensos indefinidamente. Tirei meu comentário anterior como disse (erroneamente) que despertar espúria foi a causa do problema (Não é.)Não é um exemplo de fila, mas extremamente simples :)
Alguns pontos importantes:
1) NUNCA faça
Sempre use while (condição), porque
while(!pizzaExists){ wait(); }
.2) Você deve segurar o bloqueio (sincronizado) antes de chamar wait / nofity. Os threads também precisam adquirir bloqueio antes de acordar.
3) Tente evitar adquirir qualquer bloqueio dentro do bloco sincronizado e tente não invocar métodos alienígenas (métodos que você não sabe ao certo o que estão fazendo). Se for necessário, tome medidas para evitar conflitos.
4) Tenha cuidado com notify (). Continue com notifyAll () até saber o que está fazendo.
5) Por último, mas não menos importante, leia Java Concurrency in Practice !
fonte
pizzaArrived
bandeira? se o sinalizador for alterado sem uma chamadanotify
, não terá nenhum efeito. Também apenas comwait
enotify
chama o exemplo funciona.synchronized
palavra-chave, é redundante para declarar a variávelvolatile
, e recomenda-se para evitá-lo, para evitar confusão @mridaMesmo que você tenha solicitado
wait()
enotify()
especificamente, sinto que essa citação ainda é importante o suficiente:Josh Bloch, Effective Java 2nd Edition , Item 69: Prefere utilitários de simultaneidade como
wait
enotify
(ênfase dele):fonte
notify()
ewait()
novamenteVocê deu uma olhada neste tutorial em Java ?
Além disso, aconselho que você evite brincar com esse tipo de coisa em software real. É bom brincar com ele para que você saiba o que é, mas a concorrência tem armadilhas em todo o lugar. É melhor usar abstrações de nível superior e coleções sincronizadas ou filas JMS se você estiver criando um software para outras pessoas.
É pelo menos o que eu faço. Como não sou especialista em concorrência, evito manipular os threads manualmente sempre que possível.
fonte
Exemplo
fonte
Exemplo para wait () e notifyall () em Threading.
Uma lista de matriz estática sincronizada é usada como recurso e o método wait () é chamado se a lista de matriz estiver vazia. O método notify () é chamado depois que um elemento é adicionado à lista de matrizes.
fonte
if(arrayList.size() == 0)
, acho que pode ser um erro aqui.