我试图模拟这个场景:数组中的线程可以作为数组中的顺序一个接一个地执行。我的想法是让thread[i]
始终通知thread[(i+1)%threads.length]
。
然而,代码将被卡在某处:
public class OrderedOperationThreadOneLock {
//<- Assign tasks to workers
private static final String[] message1 = {"A", "D", "G"};
private static final String[] message2 = {"B", "E", "H"};
private static final String[] message3 = {"C", "F", "I"};
private static final Task task1 = new Task(message1);
private static final Task task2 = new Task(message2);
private static final Task task3 = new Task(message3);
private static Thread[] workers = {
new Thread(task1),
new Thread(task2),
new Thread(task3)
}; //->
private static void printThreadsStatus() {
for (int i = 0; i < workers.length; ++i) {
System.out.println("Thread: " + workers[i].getName() +
"\tPriority: " + workers[i].getPriority() +
"\tStatus: " + workers[i].getState()
);
}
System.out.println("----------------------------------------------");
}
// Print a message then wait for other thread notify.
// Until all the messages are outputted.
private static class Task implements Runnable {
private final String[] messages;
Task(String[] messages) {
this.messages = Arrays.copyOf(messages, messages.length);
}
@Override
public void run() {
for (int i = 0; i < messages.length; ++i) {
System.out.println(messages[i]);
Thread curThread = Thread.currentThread();
// Stuck here
synchronized (curThread) {
try {
curThread.wait();
printThreadsStatus();
} catch (InterruptedException e) {
System.err.println("Oh, Why?");
}
}
wakeUpNext();
}
}
// `thread[i]` always notify `thread[(i+1)%threads.length]`
private void wakeUpNext() {
Thread curThread = Thread.currentThread();
int i = 0;
boolean isNotified = false;
while (i < workers.length && !isNotified) {
if (curThread.getId() == workers[i].getId()) {
int toNotify = (i+1) % workers.length;
System.out.println("Worker " + i + " is waking up " + toNotify);
synchronized (workers[toNotify]) {
workers[toNotify].notify();
}
isNotified = true;
}
i++;
}
}
}
public static void main(String... args) throws InterruptedException {
for (int i = 0; i < workers.length; ++i) {
workers[i].start();
Thread.sleep(10); // Ensure start in 1,2,3 sequence
}
System.out.println("================================");
printThreadsStatus();
synchronized (workers[0]) {
workers[0].notify();
printThreadsStatus();
}
for (int i = 0; i < workers.length; ++i) {
workers[i].join(5000);
printThreadsStatus(); // All the threads are in WAITING status...
}
printThreadsStatus();
}
}
但是,如果我使用Task
作为监视器锁定。然后该程序将不会被卡住:
public class OrderedOperationThreadSingleLock {
//<- Assign tasks to workers
private static final String[] message1 = {"A", "D", "G"};
private static final String[] message2 = {"B", "E", "H"};
private static final String[] message3 = {"C", "F", "I"};
private static final Task[] tasks = {
new Task(message1),
new Task(message2),
new Task(message3)
};
private static Thread[] workers = {
new Thread(tasks[0]),
new Thread(tasks[1]),
new Thread(tasks[2])
};//->
private static void printThreadsStatus() {
for (int i = 0; i < workers.length; ++i) {
System.out.println("Thread: " + workers[i].getName() +
"\tPriority: " + workers[i].getPriority() +
"\tStatus: " + workers[i].getState()
);
}
System.out.println("----------------------------------------------");
}
private static class Task implements Runnable {
private final String[] messages;
Task(String[] messages) {
this.messages = Arrays.copyOf(messages, messages.length);
}
@Override
public void run() {
for (int i = 0; i < messages.length; ++i) {
System.out.println(messages[i]);
synchronized (this) {
try {
wait();
printThreadsStatus();
} catch (InterruptedException e) {
System.err.println("Oh, Why?");
}
}
wakeUpNext();
}
}
// Use task as the lock instead of thread object
private void wakeUpNext() {
int i = 0;
boolean isNotified = false;
Thread t = Thread.currentThread();
while (i < workers.length && !isNotified) {
if (t.getId() == workers[i].getId()) {
int toNotify = (i+1) % workers.length;
System.out.println("Worker " + i + " is waking up " + toNotify);
synchronized (tasks[toNotify]) {
System.out.println("Prepare Waking up " + toNotify);
tasks[toNotify].notify();
}
isNotified = true;
}
i++;
}
}
}
public static void main(String... args) throws InterruptedException {
for (int i = 0; i < workers.length; ++i) {
workers[i].start();
Thread.sleep(10);
}
System.out.println("================================");
printThreadsStatus();
synchronized (tasks[0]) {
tasks[0].notify();
printThreadsStatus();
}
for (int i = 0; i < workers.length; ++i) {
workers[i].join(5000);
printThreadsStatus();
}
printThreadsStatus();
}
}
我的问题是为什么线程对象会阻止执行而任务不会?
答案 0 :(得分:1)
有趣的是问题出在workers[i].join(5000);
。方法join
也在线程上同步并调用wait
,因此当您在wakeUpNext
中通知线程时,您会错误地唤醒它。
它发生在下一个方向。
join
,导致主线程休眠。join
方法准备好虚假唤醒答案 1 :(得分:0)
可能导致问题的情况是这样的。