使用单独的线程时,RequestCycle变为null

时间:2013-12-23 16:42:06

标签: multithreading wicket

我使用单独的线程来处理文件的处理。主线程保存表以使用listView显示给用户,并使用AjaxSelfUpdatingTimer每秒刷新列表。

问题是,在我的CSV文件中处理了大约100行后,我一直收到no requestCyle异常:

Exception in thread "Thread-12" org.apache.wicket.WicketRuntimeException: No RequestCycle is currently set!
    at org.apache.wicket.Component.getRequest(Component.java:1804)
    at org.apache.wicket.markup.html.WebPage.dirty(WebPage.java:318)
    at org.apache.wicket.Page.dirty(Page.java:249)
    at org.apache.wicket.Page.componentStateChanging(Page.java:926)
    at org.apache.wicket.Component.addStateChange(Component.java:3528)
    at org.apache.wicket.Component.error(Component.java:1225)
    at com.wicket.BulkLoadPage$BatchLoaderProcessingThread.processLine(BulkLoadPage.java:806)
    at com.wicket.BulkLoadPage$BatchLoaderProcessingThread.run(BulkLoadPage.java:674)
    at java.lang.Thread.run(Thread.java:662)

这些是线程调用的可运行类:

class BatchLoaderProcessingThread implements Runnable
{
    @Override
    public void run()
    {
        processLine();
        loaderFinished();
    }   

    public void cancelThread()
    {
        cancelLoaderThread = true;
    }   
}       

class BatchDeleteProcessingThread implements Runnable
{
    @Override
    public void run()
    {
        processLine();
        deleterFinished();
    }   

    public void cancelThread()
    {
        cancelDeleterThread = true;
    }   
}

我不明白为什么requestCycle会变为null ..如何防止这种情况发生?

编辑:

注释掉反馈消息确实排除了requestRecycle错误,我收到此错误:

    java.io.IOException: Read error
at java.io.FileInputStream.readBytes(Native Method)
at java.io.FileInputStream.read(FileInputStream.java:220)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
at java.io.InputStreamReader.read(InputStreamReader.java:167)
at java.io.BufferedReader.read1(BufferedReader.java:185)
at java.io.BufferedReader.read(BufferedReader.java:261)
at java.io.BufferedReader.fill(BufferedReader.java:136)

    Dec 30 13:14:31 ERROR BulkLoadPage-java.io.IOException: Read error
at java.io.BufferedReader.readLine(BufferedReader.java:299)
at java.io.BufferedReader.readLine(BufferedReader.java:362)
at au.com.bytecode.opencsv.CSVReader.getNextLine(CSVReader.java:266)
at au.com.bytecode.opencsv.CSVReader.readNext(CSVReader.java:233)
at com..wicket.BulkLoadPage.processLine(BulkLoadPage.java:547)
at com..wicket.BulkLoadPage.access$0(BulkLoadPage.java:532)
at     

    com..wicket.BulkLoadPage$BatchLoaderProcessingThread.run(BulkLoadPage.java:1294)
at java.lang.Thread.run(Thread.java:662)

此错误仅在较大的文件中发生。 csv中的所有行都是重复的,只能模仿一个大文件..所以所有行都是相同的,不应该直接从文件引起错误。还有其他我应该寻找的东西会导致这个错误使用另一个线程吗?

3 个答案:

答案 0 :(得分:1)

RequestCycle是一个线程本地单例。如果你在另一个线程中运行一个进程,那真的意味着你的新线程中不存在RequestCycle单例。

以下示例放在https://repo.twinstone.org/projects/WISTF/repos/wicket-examples-1.4/browse

这个想法是关于保持对当前RequestCycle的引用,你不能调用RequestCycle.get(),因为线程本地单例在任何其他线程中都不存在。此CustomRequestCycle实现一直等到新线程通知其完成。循环计数器只是一个保护,如果WaitingRunnable停止工作/冻结,不冻结主线程。

测试结果:

检查您的日志案例1 ,单独的线程即将完成, RequestCycle不等待分离

http://localhost:8080/wicket-examples14/wait?millis=10
DEBUG - CustomRequestCycle         - Waiting until notify: 0
INFO  - WaitingRunnableNotifier    - Separate thread waiting finished cz.wicketstuff.enteam.wicket.examples14.request.ThreadWaitingPage
INFO  - CustomRequestCycle         - Notifier returned: Successfully finished

检查您的日志案例2 ,单独的帖子正在及时完成, RequestCycle必须等待分离

http://localhost:8080/wicket-examples14/wait?millis=3000
DEBUG - CustomRequestCycle         - Waiting until notify: 0
DEBUG - CustomRequestCycle         - Waiting until notify: 1
DEBUG - CustomRequestCycle         - Waiting until notify: 2
INFO  - WaitingRunnableNotifier    - Separate thread waiting finished cz.wicketstuff.enteam.wicket.examples14.request.ThreadWaitingPage
INFO  - CustomRequestCycle         - Notifier returned: Successfully finished

检查您的日志案例3 ,单独的帖子正在按时完成, RequestCycle已经分离

http://localhost:8080/wicket-examples14/wait?millis=10000
DEBUG - CustomRequestCycle         - Waiting until notify: 0
DEBUG - CustomRequestCycle         - Waiting until notify: 1
DEBUG - CustomRequestCycle         - Waiting until notify: 2
DEBUG - CustomRequestCycle         - Waiting until notify: 3
DEBUG - CustomRequestCycle         - Waiting until notify: 4
DEBUG - CustomRequestCycle         - Waiting until notify: 5
DEBUG - CustomRequestCycle         - Waiting until notify: 6
DEBUG - CustomRequestCycle         - Waiting until notify: 7
INFO  - CustomRequestCycle         - Notifier returned: null
INFO  - WaitingRunnableNotifier    - Separate thread waiting finished cz.wicketstuff.enteam.wicket.examples14.request.ThreadWaitingPage

<强>来源:

<强> WicketApplication

@Override
public RequestCycle newRequestCycle(Request request, Response response) {
    return new CustomRequestCycle(this, (WebRequest)request, (WebResponse)response);
}

<强> CustomRequestCycle

public class CustomRequestCycle extends WebRequestCycle implements INotifier<String> {

    private static final Logger log = LoggerFactory.getLogger(CustomRequestCycle.class);

    private long sleepTime = 1000L;
    private long maxLoops = 8;

    private boolean canDetach = true;
    private String notifierResult;

    public CustomRequestCycle(WicketApplication application, WebRequest request,
            Response response) {
        super(application, request, response);
    }

    public void notifyAny(String payload) {
        notifierResult = payload;
        canDetach = true;
    }

    @Override
    public void detach() {
        long counter = 0;
        while(!canDetach && maxLoops > counter) {
            log.debug("Waiting until notify: " + counter);
            try {
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                // do nothing
            }
            counter++;
        }
        log.info("Notifier returned: " + notifierResult);
        super.detach();
    }

    public static CustomRequestCycle get() {
        return (CustomRequestCycle)RequestCycle.get();
    }

    /**
     * @return the canDetach
     */
    public boolean isCanDetach() {
        return canDetach;
    }

    /**
     * @param canDetach the canDetach to set
     */
    public void setCanDetach(boolean canDetach) {
        this.canDetach = canDetach;
    }


}

<强> WaitingRunnableNotifier

public class WaitingRunnableNotifier implements Runnable {

    private static final Logger log = LoggerFactory.getLogger(WaitingRunnableNotifier.class);

    private final long waitTime;

    private RequestCycle requestCycle;
    private INotifier<String> notifier;

    public WaitingRunnableNotifier(RequestCycle requestCycle, long waitTime, INotifier<String> notifier) {
        super();
        this.notifier = notifier;
        this.requestCycle = requestCycle;
        this.waitTime = waitTime;
    }

    public void run() {
        String message = null;
        try {
            try {
                Thread.sleep(waitTime);
            } catch (InterruptedException e) {
            }           
            log.info("Separate thread waiting finished " + requestCycle.getResponsePageClass().getCanonicalName());
            message = "Successfully finished";
        } catch (Exception e) {
            log.error("Exception during WaitingRunnableNotifier.run()", e);
            message = "Exception: " + e.getMessage();
        } finally {
            notifier.notifyAny(message);
            clean();
        }       
    }

    /**
     * Clean object references
     */
    private void clean() {
        requestCycle = null;
        notifier = null;        
    }   

}

ThreadWaitingPage 是一个参数为“millis”的页面。在那里你可以调用另一个线程并等待它完成。

public class ThreadWaitingPage extends WebPage {

    private static final long serialVersionUID = 1L;

    private final long millis;

    public ThreadWaitingPage(final PageParameters parameters) {
        super(parameters);
        millis = parameters.getLong("millis");
        add(new Label("millis", String.valueOf(millis)));
    }

    @Override
    protected void onInitialize() {
        super.onInitialize();
        CustomRequestCycle requestCycle = CustomRequestCycle.get();
        requestCycle.setCanDetach(false);
        new Thread(new WaitingRunnableNotifier(requestCycle, millis, requestCycle)).start();

    }

}

答案 1 :(得分:1)

理想情况下,您不应该创建自己的线程,应该让容器为您完成所有这些操作。当然,这意味着您不能让请求之间在后台运行批量进程。

我之前做过类似的事情(违反我刚刚提到的规则!),最简单的方法是让Wicket脱离你自己的线程。例如,如果您的线程填充了List,并且您的ajax计时器回调从列表中提取记录并将它们添加到列表视图中。

列表视图的问题是您必须将父组件添加到ajax请求目标,这意味着整个列表视图将在ajax响应中发回,而不仅仅是您添加的新条目,而是所有旧的,这将破坏逐步加载它们的点。

作为唯一的CSV文件,您可能更好地使用数据表,并编写数据提供程序以打开文件并只读取该请求的记录,这应该更简单。

答案 2 :(得分:1)

我暂时修复了这个问题,首先将行加载到主线程中的字符串ArrayList中,然后从线程内的该列表中读取..

我真的没有看到任何延迟问题,所以这可能被认为是一个不错的选择吗?如果不是,请让我知道,否则希望这有助于其他人在某一天与此斗争。

我也在这里发布了我的问题: http://apache-wicket.1842946.n4.nabble.com/MultiThreading-issues-with-Wicket-td4663325.html#a4663389

并获得了一些很好的资源链接以供审核:

wicket中的多线程:

http://javathoughts.capesugarbird.com/2008/04/spawning-thread-for-lengthy-operation.html

RequestCycle: http://wicket.apache.org/guide/guide/chapter8.html