Java:类完全在第二个线程/IllegalMonitorStateException中运行
当您希望某个任务由另一个线程执行时,可以扩展线程或实现Runnable 我试图创建一个完全在第二个线程中运行类的类 这意味着您可以调用立即返回并由第二个线程执行的任何方法 以下是我的尝试:Java:类完全在第二个线程/IllegalMonitorStateException中运行,java,multithreading,concurrency,Java,Multithreading,Concurrency,当您希望某个任务由另一个线程执行时,可以扩展线程或实现Runnable 我试图创建一个完全在第二个线程中运行类的类 这意味着您可以调用立即返回并由第二个线程执行的任何方法 以下是我的尝试: import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQue
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Extend this class to run method calls asynchronously in the second thread implemented by this class.
* Create method(type1 param1, type2 param2, ...) and let it call this.enqueueVoidCall("method", param1, param2, ...)
*
* The thread executing the run-method will automatically call methodAsync with the specified parameters.
* To obtain the return-value, pass an implementation of AsyncCallback to this.enqueueCall().
* AsyncCallback.returnValue() will automatically be called upon completion of the methodAsync.
*
*/
public class ThreadedClass extends Thread {
private static Object test;
private Queue<String> queue_methods = new ConcurrentLinkedQueue<String>();
private Queue<Object[]> queue_params = new ConcurrentLinkedQueue<Object[]>();
private Queue<AsyncCallback<? extends Object>> queue_callback = new ConcurrentLinkedQueue<AsyncCallback<? extends Object>>();
private volatile boolean shutdown = false;
/**
* The run method required by Runnable. It manages the asynchronous calls placed to this class.
*/
@Override
public final void run() {
test = new Object();
while (!shutdown) {
if (!this.queue_methods.isEmpty()) {
String crtMethod = queue_methods.poll();
Object[] crtParamArr = queue_params.poll();
String methodName = crtMethod + "Async";
Method method;
try {
method = this.getClass().getMethod(methodName);
try {
Object retVal = method.invoke(this, crtParamArr);
AsyncCallback<? extends Object> crtCallback = queue_callback.poll();
crtCallback.returnValue(retVal);
} catch (Exception ex) {}
} catch (SecurityException ex) {
} catch (NoSuchMethodException ex) {}
} else {
try {
synchronized(test ) {
test.wait();
}
} catch (InterruptedException ex) {
System.out.println("READY");
} catch (Exception ex) {
System.out.println("READY, but " + ex.getMessage());
}
}
}
}
/**
* Asynchronously adds a method-call to the scheduler, specified by methodName with passed parameters
* @param methodName The name of the currently called method. methodName + "Async" is being called
* @param parameters Parameters you may want to pass to the method
*/
protected final void enqueueVoidCall(String methodName, Object... parameters) {
List<Object> tmpParam = new ArrayList<Object>();
for (Object crt : parameters) {
tmpParam.add(crt);
}
queue_methods.add(methodName);
queue_params.add(parameters);
queue_callback.add(null);
test.notifyAll();
}
/**
* Asynchronously adds a method-call to the scheduler, specified by methodName with passed parameters
* @param methodName The name of the currently called method. methodName + "Async" is being called
* @param callBack An instance of AsyncCallback whose returnValue-method is called upon completion of the task.
* @param parameters Parameters you may want to pass to the method
*/
protected final void enqueueCall(String methodName, AsyncCallback<? extends Object> callBack, Object... parameters) {
List<Object> tmpParam = new ArrayList<Object>();
for (Object crt : parameters) {
tmpParam.add(crt);
}
queue_methods.add(methodName);
queue_params.add(parameters);
queue_callback.add(callBack);
test.notifyAll();
}
/**
* Complete the currently running task, optionally return values and eventually shut down. The instance of this object becomes unusable after this call.
*/
public void shutdown() {
shutdown=true;
}
}
以及开始工作的主要方法:
public class TestingClass {
public static void main(String[] args) {
MySecondTask test = new MySecondTask();
test.start();
System.out.println("1. Thread [1]");
// CORRECTION, SHOULD BE:
test.test1();
// INSTEAD OF:
// test.test1Async();
for(int q=0; q<=100000; q++) {
System.out.println("1:"+ new Date().getTime()+":"+ q);
if ((q % 1000) == 0) {
System.out.flush();
}
}
System.err.println("1. Thread [2]");
}
}
不知何故,第二个线程的输出总是首先完全显示,然后其他线程在控制台上输出。如果线程同时运行,这是预期的结果,那么控制台输出应该是混合的
任何想法,以及意见,以改善我的编码风格表示感谢
编辑:
所提到的问题已经完全解决了
现在,我在调用ThreadedClass.notifyAll的行上收到一个IllegalMonitorStateException
也许我把锁弄错了
但是a为什么需要使用synchronized around wait以及如何调用notifyAll来取消阻塞wait
提前感谢并致以最良好的问候
p、 s:你们在堆栈溢出方面都做得很好。你已经在不知不觉中帮了我很多次了,谢谢你。坚持下去 主线程等待test.test1Async返回,您在那里做什么?Java线程在不同的机器上运行不同。有些机器先发制人,有些则不然。如果第二个线程在第一个线程之前输出其内容,那么运行代码的机器很可能是非抢占式的。如果您需要同步线程,有很多方法可以做到这一点,但是如果您不关心同步,那么就无法保证线程将如何运行 另外,test.test1Async在第一个线程中运行,而不是在第二个线程中运行。这可能是阻碍事态发展的原因 这意味着你可以打电话 立即返回的任何方法 由第二个执行 线 这听起来像是在与可赎回人、期货和执行人合作: 我不想告诉你,但你可能真的想调查一下这件事 编辑以解决下面的评论 只需使对象中的方法如下所示:
private ExecutorService executorService = Executors.newCachedThreadPool();
public Future<SomeObject> yourMethodName(String anyArguments) {
return executorService.submit(
new Callable<SomeObject>() {
public SomeObject call() {
SomeObject obj = new SomeObject();
/* Your Code Here*/;
return obj;
}
}
);
}
风格建议:查看java.lang.reflect.Proxy和InvocationHandler。
您可以实现一个InvocationHandler来避免您正在处理的一些反射内容,用户可以直接调用真正的接口方法。您正在同步调用test1Async。如果//bigjob在这里完成,那么它将不会与其他线程并行运行。您要做的是:
public class TestingClass {
public static void main(String[] args)
{
MySecondTask test = new MySecondTask();
test.start();
System.out.println("1. Thread [1]");
//test.test1Async();
for(int q=0; q<=100000; q++)
{
System.out.println("1:"+ new Date().getTime()+":"+ q);
if ((q % 1000) == 0)
{
System.out.flush();
}
}
System.err.println("1. Thread [2]");
}
}
记住,ThreadedClass.run方法将为您调用test1.testAsynch。事实上,我很惊讶你没有看到两次结果输出,或者计算混乱 您永远不会调用使用队列等的线程调度机制 我猜你是想打电话:
test.test1();
这反过来会将对test1Async的调用排入队列,但您错误地调用了:
test.test1Async();
直接在单个线程中执行整个过程
替换:
....
System.out.println("1. Thread [1]");
test.test1Async();
for(int q=0; q<=100000; q++)
{
...
与:
在编码方式上,在java编码时,使用与语句相同的开式括号,而C、C++和C中的JavaScript比你的方式更好。
也可以使用camelCase而不是带有下划线的separate_
这里有一个文档,详细介绍了。我建议您完全按照Tim在回答中建议的方式实施它 您自己的想法虽然很有创意,但却打破了许多最佳实践,使您的代码非常脆弱。要记住的事情太多了,否则它会微妙地破裂 想一想那个人会在你之后使用、维护和扩展你的代码。想一想当你需要在一年内重温这段代码时会发生什么 只是列出了一些你不应该做的事情: 直接扩展线程被认为是不好的做法,更倾向于实现Runnable 避免将方法编码为文本-它将在第一次重构时中断 test1Async应该是私有的,否则团队的新成员会直接调用它 方法命名应该是明确的-*Async通常意味着在后台进行,而实际上是相反的方式 整体脆性——假设我需要更改test1以返回int而不是void——我真的会记得更改其他方法吗?你记得一年后再做吗?
TestingClass.main中是否有错误?我看不到它调用test1的地方。等等,你从哪里得到这个:AsyncCallback对象,它不是JavaOK的一部分。好的,我可以理解第一点。但是它不应该在第一个线程中运行,因为当前在run方法中的线程应该调用它,这是我的意图。为什么从第一个线程调用它?这有什么不对……好吧,什么操作系统能够读取StackOverflow并运行JV
M today不是抢占式多线程?@Atmocreations-当您告诉第二个线程启动时,它会在run方法中开始执行,但test.test1Async不会在run方法中调用,而是在主线程的main方法中调用。即使尝试不启动第二个线程,test1Async仍将运行。谢谢提示。我已经听说过这些设施,我想我知道这些设施的用途。我的问题是,我需要为我要调用的类的每个方法创建另一个Runnable实现。还是我弄错了?我只想能够调用myObject.method1,也许稍后调用myObject.method2。这些需要立即返回并异步执行后台工作。我想通过我尝试使用呼叫队列,这个任务可以完成。。。?!看看上面的新代码块。。这种类型的实现应该足以解决大多数问题。如果你想让它变得更可怕/复杂,你可以添加反射来为你添加可调用项和线程池提交,但我不推荐。谢谢。在我看来,这看起来比我做的更复杂。我同意蒂姆的观点,这是最好的方法,除非你想进入AOP。请参阅下面我的答案,了解为什么您的方法虽然在技术上是正确的,但并不可取。看起来既复杂又干净。也许我只是需要深入研究一下遗嘱执行人之类的事情。有人知道一个关于这些具体工作原理的好教程吗?我在javadoc中找到了一些关于类/接口的东西,但也许你可以推荐一些好的文档?哎哟,是的,你是对的。我刚刚发表的评论到哪里去了?当darthcoder评论这句话时,我发现有些事情不可能是对的。经过其他一些小的修改,我设法再次运行代码。但这一次,它在我调用notifyAll的行中抛出了非法的MonitorStateException。我知道,现在调用该方法的第二个线程不是锁所有者。但这就是notify的作用,对吗?以继续等待此操作的其他线程。你知道怎么纠正吗?感谢JDK引用:[IllegalMonitorStateException-如果当前线程不是此对象监视器的所有者。]问题是调用notifyAll的线程在测试时从不同步。如果不发布stacktrace,很难猜测,但我会猜测。在方法中:调用test.notifyAll的enqueueCall;为了使用notify,所有线程都必须拥有对象锁。顺便说一句,我对新的并发框架不太了解,但我很确定它应该用来解决通知/同步这类复杂问题。我知道如何使用裸锁/同步。如果你需要更多的帮助,现在就让我来。如果是这样的话,请考虑清理你的代码,把所有的打开括号放在一行,很难按照我的方式看他们的观点,THX。现在又有问题了。。。你可能想看看我对奥斯卡·雷耶斯·安瑟斯的评论,看看你的回答。好的,实现Runnable而不是扩展线程是我可以很容易接受的事情。。。将…异步设置为私有或受保护以实现可扩展性也是一个很好的观点。目前,我并不真正关心这一点,因为我只想让代码按预期运行;哦,我同意,也许是布列特。但是当我按照Tim建议的方式实现它时,我想我会再次遇到同样的问题,记住在更改代码时我必须做的不同更改。你有一个很好的积极态度,这很酷:关于Tim的解决方案,优点是它非常集中。例如,如果你想改变某个方法,你就去改变它,就是这样。无需转到其他方法,无需在某处更新某些文本字符串-只需找到该方法并修复它。现在这似乎不重要,但几个月后你会感谢我:
....
System.out.println("1. Thread [1]");
test.test1Async();
for(int q=0; q<=100000; q++)
{
...
....
System.out.println("1. Thread [1]");
test.test1();
for ( int q=0; q<=100000 ; q++ ) {
....