我需要等到一个线程列表终止,但是我的代码只有在Sleep不是常数时才能工作我想知道为什么,这里我的Class测试:
如果我改变Thread.sleep(200); --->了Thread.sleep(I * B);它的工作正常!?
public class TestThread {
public static void main(String[] args) {
Object lock = new Object();
for ( int p=0; p<10; p++) {
final int i=p;
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("notify "+i);
synchronized(lock){
lock.notify();
}
}
}).start();
}
Integer counter=0;
synchronized (lock) {
try {
while(true){
System.out.println("Before wait");
if (counter==10)//wait until all threads ends
break;
lock.wait();
counter += 1;
System.out.println("After wait "+counter);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("End");
}
}
notifyAll()
的结果Before wait
notify 3
notify 7
After wait 1
Before wait
notify 1
notify 2
notify 0
After wait 2
Before wait
notify 4
After wait 3
Before wait
After wait 4
Before wait
notify 5
After wait 5
Before wait
notify 6
notify 8
After wait 6
Before wait
notify 9
After wait 7
Before wait
After wait 8
Before wait
并且该过程未终止
答案 0 :(得分:4)
以下是发生的事情:
synchronized (lock)
lock.wait()
释放锁定并等待通知。notify
。带注释的代码:
public static void main(String[] args)
{
final Object lock = new Object();
for (int p = 0; p < 10; p++)
{
//You start each thread. They all go to sleep for 200ms.
final int i = p;
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
Thread.sleep(200);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("notify " + i);
synchronized (lock)
{
lock.notify();
}
}
}).start();
}
//At this point, all the thread are sleeping.
Integer counter = 0;
synchronized (lock)//The main thread acquire the lock, so even if the other thread wakes up, they will wait for the lock
{
try
{
while (true)
{
System.out.println("Before wait");
if (counter == 10)// wait until all threads ends
break;
lock.wait();// Object.wait() will release the lock on the object.
// So 1 of the thread will acquire the lock, call notify(), and release the lock.
// But you have no guarantee that the main thread will reacquire the lock right away !!
//its possible that all remaining waiting thread gets the lock and call notify(), before the main thread get
//a chance to continue. This is why, you may end up with a deadlock
counter += 1;
System.out.println("After wait");
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
System.out.println("End");
}
以下是处理此问题的更好方法:
final List<Thread> workers = new ArrayList<Thread>();
for (int p = 0; p < 10; p++)
{
final int i = p;
final Thread t = new Thread(new Runnable()
{
@Override
public void run()
{
try
{
Thread.sleep(200);//or do something a bit more useful
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
});
workers.add(t);
t.start();
}
for(Thread t : workers)
t.join();//wait until the thread finishes
答案 1 :(得分:1)
在我的回忆中,notify()
只会通知单个线程 - 而无法控制通知哪个线程。因此,您无法确定是否已通知正确的线程已释放锁定。
如果要确保通知所有线程,请使用notifyAll()
。
鉴于你在锁定对象上只有一个wait(),问题是你实际上有多少次等待&#34;和/或通知。一旦子进程释放锁定,就不能保证在另一个子线程继续通过它的同步块之前,父线程(正在等待)将是第一个被唤醒的线程。你的父线程等待被唤醒10次,但如果另一个线程&#34;拦截&#34;你预期的通知,你永远不会收到所有10,你的循环永远不会中断。
通过为每个线程使用不同的休眠时间,您为主线程提供了足够的时间来完成lock.wait()
之后的代码并重新输入它的while(true)
循环以等待另一个锁定时间。
如果这是使用锁定对象的练习,那就是一件事。否则,您可以使用Thread的join()
方法确保在继续之前完成所有线程。
答案 2 :(得分:0)
我得到相反的结果:) 200过程完成,但是任意低的数字都没有。
我认为发生的事情是一个或多个线程运行:
synchronized(lock){
lock.notify();
}
在被通知的线程再次运行之前。
这可能是因为: Java : Does wait() release lock from synchronized block (第一个答案)
支持cohadar “ 线程在进入同步方法时获取内部锁。 synchronized方法中的线程被设置为锁的所有者并处于RUNNABLE状态。尝试输入锁定方法的任何线程都会变为BLOCKED。
当线程调用等待时,它释放当前对象锁(它保留所有来自其他对象的锁),然后进入WAITING状态。
当某个其他线程在同一个对象上调用notify或notifyAll时,第一个线程将状态从WAITING更改为BLOCKED,Notified线程不会自动重新获取锁定或变为RUNNABLE,实际上它必须为所有其他被阻塞的线程争取锁定。 “
您的通知会将您的等待线程推送到“已阻止”状态。这是等待同步的线程等待的阻塞状态。
如果执行以下任何线程:
synchronized(lock){
lock.notify();
}
完成并运行他们的通知,然后它就不再有效了。等待线程已经被唤醒了。 所以你失去了一些通知。 (他们只会在事后发送一次)。
如果你改变睡眠持续时间的时间,你最终可能会以等待线程结束幸运者的方式进行洗牌。但这是随机的。
答案 3 :(得分:0)
为简单起见,让我们说你的main()启动两个线程并等待两次而不是启动十个线程并等待十次。这是可能发生的事情。
此时,main()将永远等待()因为没有孩子可以通知()它。