使用线程打印 arraylist 时获取 IllegalMonitorStateException

Getting IllegalMonitorStateException while printing arraylist using threads

我正在尝试使用 2 个线程打印出 arraylist 的内容,我的主要目标是让线程以同步方式读取 arraylist 并打印其内容。尽管我使用同步块,但我仍然收到 IllegalMonitorStateException。我知道这是一个基本问题,但我无法让它工作,请原谅。

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Q1 {
public static Q1 yeni;

public static void main(String[] args) {
    // TODO Auto-generated method stub
    yeni = new Q1();
}

Q1() {

    List<String> list = Collections.synchronizedList(new ArrayList<String>());

    list.add("a1");
    list.add("b1");
    list.add("c1");
    list.add("d1");
    list.add("e1");
    list.add("f1");
    ExecutorService executorService = Executors.newFixedThreadPool(10);

    synchronized (list) {
        myThread thread1 = new myThread(list);
        myThread thread2 = new myThread(list);
        thread1.start();
        thread2.start();

    }
    }

    }

这是我的线程class

import java.util.*;

public class myThread extends Thread {
List<String> liste;

public myThread(List<String> liste) {
    this.liste = liste;
}

@Override
public void run() {
    try {
        synchronized (Q1.yeni) {
            System.out.println("Thread number " + this.getName() + " started running.");
            for (int i = 0; i < liste.size(); i++) {
                System.out.println(liste.get(i));
                this.wait(3000);
            }
        }
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    }

    }

IllegalMonitorStateException 的原因是您在对象 (this) 上调用 wait 而没有持有该对象的监视器。您必须用 synchronized (this) 块包装此调用,或在 Q1.yeni 上调用 wait,此代码已同步。

但是,wait 的使用似乎有误。此方法用于等待条件,该条件通过调用同一对象上的 notifynotifyAll 来发出信号。由于此代码中没有明显的条件,也没有使用 notifynotifyAll,我怀疑您真正想要调用的是 this.sleep(3000),它会暂停线程三秒钟,然后在该持续时间结束后恢复它。

sleep 方法不需要任何监视器的所有权,也不会释放持有的监视器的所有权,因此另一个线程将无法进入 synchronized (Q1.yeni) 块,而另一个线程正在休眠.这意味着进入该块的第一个线程将 运行 完成,在第二个线程有机会开始之前迭代整个列表。尚不完全清楚这是否是此处的意图。

有关更多使用信息,请参阅 Object.wait and Thread.sleep 的文档。

第二个问题是 Q1.yeni 在必须初始化之前被这些线程访问,因为线程是在 Q1 构造函数中启动的,语句 yeni = new Q1(); 只分配yeni 构造函数完成后。在这种情况下,线程使用 synchronized (liste) 可能会更好。

除此之外,在 Q1 构造函数中包含 synchronized (list) 并没有完成多少,因为主线程不访问或操作该部分中 list 的内容。唯一的实际影响是,它启动的线程在到达对 liste.size() 的第一次调用时会阻塞,直到主线程退出 synchronized (list)(在启动两个线程后立即)。这可能会稍微减慢运行的第一个线程,但对线程安全或程序的正确性没有影响。

我还建议查看 "How to Handle InterruptedException in Java"。在这种情况下,我建议在异常处理程序中恢复中断状态。

总而言之,这是此代码的修订示例(包括删除未使用的代码和样板注释、改进格式并确保与 Java 命名约定一致的其他小改动):

Q1.java:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Q1 {
    private static Q1 yeni;

    public static void main(String[] args) {
        yeni = new Q1();
    }

    Q1() {
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        list.add("a1");
        list.add("b1");
        list.add("c1");
        list.add("d1");
        list.add("e1");
        list.add("f1");

        MyThread thread1 = new MyThread(list);
        MyThread thread2 = new MyThread(list);
        thread1.start();
        thread2.start();
    }
}

MyThread.java:

import java.util.*;

public class MyThread extends Thread {
    private final List<String> liste;

    public MyThread(List<String> liste) {
        this.liste = liste;
    }

    @Override
    public void run() {
        try {
            synchronized (liste) {
                System.out.println("Thread number " + this.getName() + " started running.");
                for (int i = 0; i < liste.size(); i++) {
                    System.out.println(liste.get(i));
                    sleep(3000);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            interrupt();
        }
    }
}

输出:

Thread number Thread-0 started running.
a1
b1
c1
d1
e1
f1
Thread number Thread-1 started running.
a1
b1
c1
d1
e1
f1