问题
How I can call a particular thread in inter-thread communication?
In the program below I have two threads t1
and t2
.
When I call t1.notify()
it raises:
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at Shared.methodTwo(NotifyThread.java:43)
at Thread2.run(NotifyThread.java:77)
Error
class Shared {
Thread1 t1 ;
Thread2 t2 ;
void ThreadInit( Thread1 t1 , Thread2 t2 ) {
this.t1 = t1 ;
this.t2 = t2 ;
}
synchronized void methodOne()
{
Thread t = Thread.currentThread();
System.out.println(t.getName()+" is relasing the lock and going to wait");
try
{
wait(); //releases the lock of this object and waits
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println(t.getName()+" got the object lock back and can continue with it's execution");
}
synchronized void methodTwo()
{
Thread t = Thread.currentThread();
try
{
Thread.sleep(5000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
t1.notify();
System.out.println("A thread which is waiting for lock of this object is notified by "+t.getName());
}
}
class Thread1 extends Thread
{
Shared s ;
Thread1( Shared s ) {
this.s = s ;
}
public void run()
{
s.methodOne(); //t1 calling methodOne() of 's' object
}
}
class Thread2 extends Thread {
Shared s ;
Thread2( Shared s ) {
this.s = s ;
}
public void run()
{
s.methodTwo(); //t1 calling methodOne() of 's' object
}
}
public class NotifyThread
{
public static void main(String[] args)
{
final Shared s = new Shared();
Thread1 t1 = new Thread1(s) ;
Thread2 t2 = new Thread2(s) ;
s.ThreadInit(t1,t2) ;
t1.start();
t2.start();
}
}
回答1:
You don't / can't notify a specific thread. You call notify()
on a lock object. This wakes up one of the threads1 that is waiting on the lock. In your case, the lock object is a Thread
... which rather confuses the picture. However, see below.
But your problem (the IllegalMonitorStateException
) happens because the thread doing the notifying (i.e. the current thread) does not hold the lock. It is a (hard) requirement that the current thread must hold the lock when it notifies a lock.
For more details, read the javadocs for Object.wait(timeout)
or (for example) this: http://howtodoinjava.com/core-java/multi-threading/how-to-work-with-wait-notify-and-notifyall-in-java/
1 - If multiple threads are waiting on your lock, one thread is chosen "randomly" by the scheduler. Alternatively notifyAll will wake up all of the waiting threads.
I would NOT use a Thread
object as a lock object. It will possibly work, but there is also a chance that something else (maybe something in the runtime system) is also locking / waiting / notifying the Thread
objects. Then things would get very confusing.
(Indeed, read the javadoc for Thread.join(long)
!)
It is BETTER to create lock objects specifically for this purpose; e.g.
private final Object lock = new Object();
Also, writing classes that extend Thread
is usually a bad idea. It is usually better to implement the Runnable
interface, instantiate it, and pass the instance as a parameter to the Thread
constructor; e.g.
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("Hello world");
}});
t.start();
One advantage of implementing Runnable
rather than extending Thread
is that you can use your code more easily with something that manages the thread life cycles for you; e.g. an ExecutorService
, a fork-join thread pool or a classic thread pool.
A second one is that light-weight thread logic can be implemented concisely as an anonymous class ... as in my example.
回答2:
To add some points;
Your code is using intrinsic locks. Every object in the JVM has its own lock. This lock has nothing to do with the functionality of the object. Acquiring the lock by itself does nothing (in the absence of further measures like using the synchronized keyword) to prevent other threads from monkeying with the object's contents. Calling notify on a thread does not mean that that particular thread will receive a notification.
As said previously acquiring the lock on Thread objects is discouraged. The join method on Thread uses the intrinsic lock on the thread joined to. If code acquires a lock for different reasons then a thread can be notified for some condition that it may not care about.
The intrinsic lock is a go-between that tells the OS scheduler which threads are waiting. The OS scheduler decides which threads in the wait set for the lock get notified. When a thread calls notify on an object, it is telling the lock on that object to tell the scheduler to choose which waiting thread gets notified. The lock knows which threads are waiting but it doesn't know what condition they're waiting for. (ReentrantLock is a big improvement for this, see the API doc for Condition.)
Of course notifyAll wakes up all the threads in the wait set, but again that is something the lock and the scheduler know. The thread calling notifyAll doesn't know about what threads are waiting. The system is designed intentionally so that threads cannot notify other threads directly.
Yet another thing here is that calling wait with no check for a condition is unreliable. If a thread doesn't happen to have acquired a lock before a notification is made, it misses that notification. If a thread receives a notification, that is no guarantee it will get the lock next, another thread could act and invalidate the state that the notified thread is expecting. Always have some internal state that the current thread can check to verify that the object's state is what the thread is expecting, and make that check as the test in a loop.
For instance if I have a fixed size blocking queue (implemented using a list internally, protecting it from concurrent access using synchronization) where threads try to take something from the queue but block if the queue is empty, the take method could look like:
public synchronized T take() throws InterruptedException {
while (list.isEmpty()) {
wait();
}
notifyAll();
return list.remove(0);
}
Once a waiting thread wakes and reacquires the lock, it checks to see if the current situation is what it has been waiting for. Only if that is the case should the thread exit the loop and proceed.
回答3:
Here you can find out the nice example how to use wait and notify or notifyAll() – Niraj 2 days ago
If you are using notify() instead of notifyAll() it will triger only one thread in wait() state with high priority. If you are using notifyAll() it will triger all the thread which was in wait() state.
package com.construction.house;
import com.construction.labours.LabourCement;
import com.construction.labours.LabourPainting;
import com.construction.labours.LabourPolishMarbel;
public class House implements Runnable{
public House() {
// TODO Auto-generated constructor stub
}
public static void main(String[] args) {
// TODO Auto-generated method stub
House myHouse = new House();
LabourCement labourCement = new LabourCement(myHouse,"Cement");
labourCement.start();
LabourPainting labourPaining = new LabourPainting(myHouse,"Painter");
labourPaining.start();
LabourPolishMarbel labourPolish = new LabourPolishMarbel(myHouse,"PolishMan");
labourPolish.start();
}
boolean isPolished = false,isCemented = false,isPaited = false;
public synchronized void workAsDemand() throws InterruptedException {
if (!isPolished) {
isPolished = true;
System.out.println(Thread.currentThread().getName()+"--->>Polish in progress");
wait();
System.out.println(Thread.currentThread().getName()+"--->>Polish Completed");
}
else if (!isPaited) {
System.out.println(Thread.currentThread().getName()+"--->>Painting house in Progress");
isPaited = true;
//notify();
wait();
System.out.println(Thread.currentThread().getName()+"--->>Painting house in Completed");
}
else if (!isCemented) {
System.out.println(Thread.currentThread().getName()+"---->>Cemented house");
isCemented = true;
notifyAll();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
workAsDemand();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package com.construction.labours;
public class LabourCement extends Thread {
public LabourCement() {
// TODO Auto-generated constructor stub
}
public LabourCement(Runnable arg0) {
super(arg0);
// TODO Auto-generated constructor stub
}
public LabourCement(String arg0) {
super(arg0);
// TODO Auto-generated constructor stub
}
public LabourCement(ThreadGroup arg0, Runnable arg1) {
super(arg0, arg1);
// TODO Auto-generated constructor stub
}
public LabourCement(ThreadGroup arg0, String arg1) {
super(arg0, arg1);
// TODO Auto-generated constructor stub
}
public LabourCement(Runnable arg0, String arg1) {
super(arg0, arg1);
// TODO Auto-generated constructor stub
}
public LabourCement(ThreadGroup arg0, Runnable arg1, String arg2) {
super(arg0, arg1, arg2);
// TODO Auto-generated constructor stub
}
public LabourCement(ThreadGroup arg0, Runnable arg1, String arg2, long arg3) {
super(arg0, arg1, arg2, arg3);
// TODO Auto-generated constructor stub
}
}
package com.construction.labours;
public class LabourPolishMarbel extends Thread {
public LabourPolishMarbel() {
// TODO Auto-generated constructor stub
}
public LabourPolishMarbel(Runnable arg0) {
super(arg0);
// TODO Auto-generated constructor stub
}
public LabourPolishMarbel(String arg0) {
super(arg0);
// TODO Auto-generated constructor stub
}
public LabourPolishMarbel(ThreadGroup arg0, Runnable arg1) {
super(arg0, arg1);
// TODO Auto-generated constructor stub
}
public LabourPolishMarbel(ThreadGroup arg0, String arg1) {
super(arg0, arg1);
// TODO Auto-generated constructor stub
}
public LabourPolishMarbel(Runnable arg0, String arg1) {
super(arg0, arg1);
// TODO Auto-generated constructor stub
}
public LabourPolishMarbel(ThreadGroup arg0, Runnable arg1, String arg2) {
super(arg0, arg1, arg2);
// TODO Auto-generated constructor stub
}
public LabourPolishMarbel(ThreadGroup arg0, Runnable arg1, String arg2, long arg3) {
super(arg0, arg1, arg2, arg3);
// TODO Auto-generated constructor stub
}
}
package com.construction.labours;
public class LabourPainting extends Thread {
public LabourPainting() {
// TODO Auto-generated constructor stub
}
public LabourPainting(Runnable arg0) {
super(arg0);
// TODO Auto-generated constructor stub
}
public LabourPainting(String arg0) {
super(arg0);
// TODO Auto-generated constructor stub
}
public LabourPainting(ThreadGroup arg0, Runnable arg1) {
super(arg0, arg1);
// TODO Auto-generated constructor stub
}
public LabourPainting(ThreadGroup arg0, String arg1) {
super(arg0, arg1);
// TODO Auto-generated constructor stub
}
public LabourPainting(Runnable arg0, String arg1) {
super(arg0, arg1);
// TODO Auto-generated constructor stub
}
public LabourPainting(ThreadGroup arg0, Runnable arg1, String arg2) {
super(arg0, arg1, arg2);
// TODO Auto-generated constructor stub
}
public LabourPainting(ThreadGroup arg0, Runnable arg1, String arg2, long arg3) {
super(arg0, arg1, arg2, arg3);
// TODO Auto-generated constructor stub
}
}
来源:https://stackoverflow.com/questions/43289395/how-to-notify-a-specific-thread-in-java