我的程序应该按顺序打印出1到10的数字,使用线程(为了学习线程)。 问题是该程序陷入僵局。那是为什么?
我创建了10个这样的主题:
for (int i = 0; i < 10; i++) {
new PrintThread(i).start();
}
线程类如下所示:
class PrintThread extends Thread {
int curr;
static Integer prev;
PrintThread(int curr) {
this.curr = curr;
}
public synchronized void run() {
if (prev == null) prev = curr - 1;
while (curr != prev + 1) {
System.out.println("Waiting...");
try { wait(); }
catch (InterruptedException e){ }
System.out.println("Woke up!");
}
System.out.println(i);
prev = curr;
notifyAll();
}
}
输出
0
Waiting... (9 times)
答案 0 :(得分:1)
所有线程同步并等待自己。结果,即使一个线程通知,该通知也不会到达任何人,因为其他线程正在等待不同的监视器对象(即它们自己)。在这种情况下,所有线程都应该在公共监视器对象上进行同步和等待/通知。
您根本不应在wait
个对象上使用notify
和Thread
,因为它实际上可能会导致线程调度iirc出现死锁。
作为旁注:不要扩展Thread
,而是实现Runnable
并提供Runnable
的实例作为Thread
的构造函数的参数。
答案 1 :(得分:0)
@SotiriosDelimanolis的评论清楚地解释了这个问题。想要指出你的问题的另一种方法,检查以下程序,其中一个使用蛮力逻辑乘以时间&amp;其他人在不使用wait
的情况下等待与你的逻辑类似的逻辑。
import java.util.Set;
public class ThreadChecker {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new PrintThread2(i+1).start();
}
}
}
//Brute force method
class PrintThread extends Thread {
int curr;
static Integer prev;
PrintThread(int curr) {
this.curr = curr;
}
public synchronized void run() {
try {
Thread.sleep(curr*100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(curr);
}
}
//Logic using Thread.sleep
class PrintThread2 extends Thread {
int curr;
static Integer prev=0;
PrintThread2(int curr) {
this.curr = curr;
}
public synchronized void run() {
while(curr!=prev && curr-prev!=1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(curr);
prev = curr;
//Following is to debug the thread state
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
for(Thread str: threadSet){
System.out.println("Thread name:"+str.getName()+
" ; State:"+str.getState().toString());
}
}
}
答案 2 :(得分:0)
wait(),notify()和notifyAll()是Object类的方法。这应该由线程之间共享的对象调用。在这里,我写了一个小型的生产者 - 消费者计划,可以清除你对wait()&amp ;;通知()。
package com.mytest.example;
public class ThreadDemo {
int message;
String threadName;
boolean consumed;
ThreadDemo(int message, String threadName, boolean consumed) {
this.setMessage(message);
this.setThreadName(threadName);
this.setConsumed(consumed);
}
public static void main(String[] args) {
ThreadDemo demo = new ThreadDemo(0,"Main",true);
Thread producer = new Thread(new Producer(demo));
Thread consumer = new Thread(new Consumer(demo));
producer.start();
consumer.start();
try {
consumer.join();
} catch (InterruptedException e) {
System.out.println("ERROR:- "+e.getMessage());
}
System.out.println("Main() exit");
}
/**
* @return the message
*/
public int getMessage() {
return message;
}
/**
* @param message the message to set
*/
public void setMessage(int message) {
this.message = message;
}
/**
* @return the threadName
*/
public String getThreadName() {
return threadName;
}
/**
* @param threadName the threadName to set
*/
public void setThreadName(String threadName) {
this.threadName = threadName;
}
/**
* @return the contentAvailable
*/
public boolean isConsumed() {
return consumed;
}
/**
* @param contentAvailable the contentAvailable to set
*/
public void setConsumed(boolean consumed) {
this.consumed = consumed;
}
}
class Producer implements Runnable {
ThreadDemo demo;
Producer(ThreadDemo demo) {
this.demo = demo;
}
@Override
public void run() {
for(int i=1; i<=10; i++) {
produce(i);
}
}
/**
* This method is used to produce a value in demo object & block the other threads which are waiting for demo object.
* Once the producer finishes its task, it notifies all other waiting threads & releases the lock on demo object.
* @param value
*/
public void produce(int value) {
synchronized (demo) {
try {
while(!demo.isConsumed()) {
demo.wait();
}
if(demo.isConsumed()) {
demo.setMessage(value);
demo.setThreadName("Producer");
demo.setConsumed(false);
System.out.println(demo.getThreadName()+": "+demo.getMessage());
Thread.sleep(1000);
demo.notifyAll();
}
} catch(InterruptedException e) {
System.out.println("ERROR:- "+e.getMessage());
}
}
}
}
class Consumer implements Runnable {
ThreadDemo demo;
Consumer(ThreadDemo demo) {
this.demo = demo;
}
@Override
public void run() {
for(int i=1; i<=10;i++) {
consume();
}
}
/**
* This method checks whether any content available to consume from producer. If yes then it will acquire the lock
* on demo object and consumes the content. Once the content is consumed, it will releases the lock on demo object.
*/
public void consume() {
synchronized (demo) {
try {
while(demo.isConsumed()) {
demo.wait();
}
if(!demo.isConsumed()) {
demo.setThreadName("Consumer");
demo.setConsumed(true);
System.out.println(demo.getThreadName()+": "+demo.getMessage());
Thread.sleep(1000);
demo.notifyAll();
}
} catch(InterruptedException e) {
System.out.println("ERROR:- "+e.getMessage());
}
}
}
}
答案 3 :(得分:0)
原因可能是,根据Thread类而言,除了在特定时间间隔内执行加入保证之外,没有其他方法。 因此,在不同的时间间隔调用不同的线程,这是基于线程调度程序,我们不再完全控制这些方法。
为了更加精确,我建议观看以下视频以了解有关线程的更多信息。
https://www.youtube.com/watch?v=O_Ojfq-OIpM
希望您在观看这三个视频后得到答案
答案 4 :(得分:0)
我试图做空。
import java.util.concurrent.atomic.AtomicInteger;
public class PrintThread implements Runnable {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new PrintThread(i)).start();
}
}
private static final AtomicInteger nextToPrint = new AtomicInteger(0);
private final int curr;
public PrintThread(int curr) {
this.curr= curr;
}
@Override
public void run() {
synchronized (nextToPrint) {
while (nextToPrint.get() != curr) {
try {
nextToPrint.wait();
} catch (InterruptedException e) { /*do nothing*/ }
}
System.out.println(curr);
nextToPrint.incrementAndGet();
nextToPrint.notifyAll();
}
}
}