[英]Make even and odd threads to print numbers in natural order in Java
我知道之前已经问过这个问题,但我无法弄清楚为什么我的解决方案对我不起作用。 我有两个线程偶数和奇数,一个打印偶数和其他打印奇数。 当我启动线程时,我希望输出是自然的数字顺序,如0 1 2 3 ..等。 这是我的代码: - [更新]
public class ThreadCommunication {
public static void main(String... args) throws InterruptedException
{
final ThreadCommunication obj = new ThreadCommunication();
Thread even = new Thread(){
@Override
public void run()
{
for(int i=0;i<10;i=i+2){
synchronized(obj){
System.out.println(i);
try {
obj.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
};
Thread odd = new Thread(){
@Override
public void run()
{
for(int i=1;i<10;i=i+2){
synchronized(obj){
System.out.println(i);
obj.notify();
}
}
}
};
even.start();
odd.start();
}
}
当我运行上面的代码时,有时会按照预期自然顺序打印数字,但有时它会以其他顺序打印出来:
0
1
3
5
7
9
2
我在这做错了什么?
编辑:
volatile static boolean isAlreadyWaiting = false;
Thread even = new Thread() {
@Override
public void run() {
synchronized (obj) {
for (int i = 0; i < 10; i = i + 2) {
System.out.println(i);
try {
if (!isAlreadyWaiting) {
isAlreadyWaiting = true;
obj.wait();
}
obj.notify();
isAlreadyWaiting=false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
Thread odd = new Thread() {
@Override
public void run() {
synchronized (obj) {
for (int i = 1; i < 10; i = i + 2) {
System.out.println(i);
try {
if(isAlreadyWaiting){
obj.notify();
isAlreadyWaiting = false;
}
if (!isAlreadyWaiting) {
isAlreadyWaiting = true;
obj.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
检查文档
公共类IllegalMonitorStateException扩展RuntimeException
抛出此异常表示线程已尝试在对象的监视器上等待 ,或者在没有指定监视器的情况下通知在对象监视器上等待的其他线程。
Monitor由obj
拥有
所以你应该打电话
obj.wait();
和
obj.notify();
有关所有权的更多信息
此方法(等待或通知)只应由作为此对象监视器所有者的线程调用。 线程以三种方式之一成为对象监视器的所有者:
- 通过执行该对象的同步实例方法。
通过执行在对象上同步的synchronized语句的主体。
- 对于Class类型的对象,通过执行该类的同步静态方法。
一次只有一个线程可以拥有对象的监视器。
@Pragnani Kinnera对你看到的例外是对的。 但是如果你想在even
和odd
之间交替,你需要将第二个同步块移动到循环中。 否则,通知线程将保持锁定,直到循环完成。 (与第一个线程相反,第一个线程在每一轮产生锁定。)
Thread odd = new Thread(){
@Override
public void run()
{
for(int i=1;i<10;i=i+2){
synchronized(obj){
System.out.println(i);
notify();
}
}
}
};
但是,第一个线程应该在synchronized块内部有循环。 如果两个线程都释放锁定,则它们都有相同的机会重新获取它。 但是如果第一个循环在同步块内部,则第二个线程将无法重新进入,直到第一个循环完成整轮并再次等待。
编辑:这仍然无法正常工作,因为无法保证第一个线程在第二个线程执行之前不会重新获取锁定,根据文档中的引用:
唤醒的线程将以通常的方式与可能主动竞争同步此对象的任何其他线程竞争; 例如,唤醒线程在下一个锁定此对象的线程中没有可靠的特权或劣势。
你可能想要从两个线程唤醒并通知它们以确保它们是同步的。
这是你的解决方案:
public class ThreadCommunication {
public static void main(String args[]) throws InterruptedException
{
final ThreadCommunication obj = new ThreadCommunication();
Thread even = new Thread("Even Thread"){
@Override
public void run()
{
for(int i=0;i<10;i=i+2){
System.out.println(i);
synchronized(obj){
obj.notify();
}
synchronized(obj){
try {
obj.wait();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
Thread odd = new Thread(){
@Override
public void run()
{
for(int i=1;i<10;i=i+2){
try {
synchronized(obj){
obj.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
synchronized(obj){
obj.notifyAll();
}
}
}
};
even.start();
odd.start();
}
}
正如@shmosel所解释的,您的synchronized块应该只包含需要同步的代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.