J2ME - 如何使线程返回一个值,并在该线程完成后,在其他操作中使用返回值?

Posted

技术标签:

【中文标题】J2ME - 如何使线程返回一个值,并在该线程完成后,在其他操作中使用返回值?【英文标题】:J2ME - How to make a thread return a value and after that thread is finished, use the return value in other operations? 【发布时间】:2014-06-23 01:14:58 【问题描述】:

我有一些关于线程使用的问题,特别是当您必须等待线程完成才能执行其他操作时。

在我的应用程序中,我使用线程进行诸如 http 连接之类的操作,或者当我读取或写入 RecordStore 时。

例如,在我用于初始化线程的以下类中,我使用名为 HttpQueryCustomers 的方法从 Web 服务中检索一些客户。

    public class thrLoadCustomers implements Runnable 

    private RMSCustomer mRMSCustomer;    
    private String mUrl;

    public thrLoadCustomers(RMSCustomer rmsCust, String url)         
        mRMSCustomer = rmsCust;        
        mUrl = url;
    

    public void run() 
        String jsonResultados = "";
        try 
            jsonResultados = HttpQueryCustomers();
         catch (IOException ex) 
            //How to show a message from here??
         catch (SecurityException se) 
             //How to show a message here??
         catch (NullPointerException npe) 
             //How to show a message from here??
        
        if (!jsonResultados.equals("")) 
            try 
                mRMSCustomer.save(jsonResultados);
             catch (RecordStoreException ex) 
               //How to show a message from here???
            
        

    

    public String HttpQueryCustomers() throws IOException,SecurityException,NullPointerException 
        StringBuffer stringBuffer = new StringBuffer();
        HttpConnection hc = null;
        InputStream is = null;
        System.out.println(mUrl);
        try 
            hc = (HttpConnection) Connector.open(mUrl);

            if (hc.getResponseCode() == HttpConnection.HTTP_OK) 
                is = hc.openInputStream();
                int ch;
                while ((ch = is.read()) != -1) 
                    stringBuffer.append((char) ch);
                
            

         finally 
            is.close();
            hc.close();
        
        String jsonData = stringBuffer.toString();
        return jsonData.toString();
    

请注意,在上面的类中,我传递了一个名为 rmsCust 的参数,类型为 RMSCustomer

RMSCustomer 是我用来处理与 RMS 相关的所有操作的类:

public class RMSCustomer 

    private String mRecordStoreName;
    private Customer[] mCustomerList;

    public RMSCustomer(String recordStoreName) 
        mRecordStoreName = recordStoreName;
    

    public Customer[] getCustomers() 
        return mCustomerList;
    

    public Customer get(int index) 
        return mCustomerList[index];
      

    public void save(String data) throws RecordStoreException,JSONException,NullPointerException 
        RecordStore rs = null;
        int idNuevoRegistro;
        String stringJSON;
        try 
            rs = RecordStore.openRecordStore(mRecordStoreName, true);

            JSONArray js = new JSONArray(data); 
            //Set the size of the array
            mCustomerList = new Customer[js.length()];

            for (int i = 0; i < js.length(); i++) 

                JSONObject jsObj = js.getJSONObject(i);
                stringJSON = jsObj.toString();                   
                idNuevoRegistro = addRecord(stringJSON, rs); 
                //Add a new Customer to the array
                mCustomerList[i] = initializeCustomer(stringJSON, idNuevoRegistro);
            


         finally 
            if (rs != null) 
                rs.closeRecordStore();
            
        
    

    public int addRecord(String stringJSON, RecordStore rs) throws JSONException,RecordStoreException                     
        byte[] raw = stringJSON.getBytes();
        int idNuevoRegistro = rs.addRecord(raw, 0, raw.length);
        return idNuevoRegistro;             
       

    public Customer initializeCustomer(String stringJSON, int idRecord) throws JSONException 
        Customer c = new Customer();
        JSONObject jsonObj = new JSONObject(stringJSON);
        // Set Customer properties 
        //...
        return c;
    



该类用于显示客户列表,如您所见,它扩展了 List 类并接收客户数组作为参数。

public class ListCustomers extends List 

    private final Customer[] mData;

    public static ListCustomers create(Customer[] data) 
        int i = 0;
        for (; i < data.length; i++) 
            if (data[i] == null) 
                break;
            
        
        String[] names = new String[i];
        for (int j = 0; j < i; j++) 
            names[j] = data[j].name;
        
        return new ListCustomers(names, data);
    

    protected ListCustomers(String names[], Customer[] data) 
        super("List of Customer", IMPLICIT, names, null);
        mData = data;
    
    public Customer getSelectedObject() 
        return mData[this.getSelectedIndex()];
    

当我想显示客户列表时,这就是我从 MIDlet 调用线程的方式(使用所有 3 个之前的类):

private void showCustomerList(String url) 
        showWaitForm();
        if (scrCustomerList == null)             
            rmsCustomers = new RMSCustomer("rmsCustomers");          

            thrLoadCustomers load = new thrLoadCustomers(rmsCustomers, url);
            Thread t = new Thread(load);
            t.start();
            try 
                t.join();
             catch (InterruptedException ex) 

            
            scrCustomerList = ListCustomers.create(rmsCustomers.getCustomers());
            scrCustomerList.addCommand(cmdSelect);
            scrCustomerList.addCommand(cmdBack);
            scrCustomerList.setCommandListener(this);
        
        mDisplay.setCurrent(scrCustomerList);
    

现在这是我遇到的问题:

    showWaitForm() 不起作用(它设置了一个带有 Gauge 的表单作为 当前表格) 我不知道如何显示所有可能引发的异常 在 thrLoadCustomers 类中。 不知道用t.join()是不是最好的选择 最后一个问题是关于book I'm reading 所说的:

线程尤其是稀缺商品。 MSA 规范要求必须允许应用程序创建 十个线程。仅仅因为你可以并不意味着你应该。一般来说, 尝试使用尽可能少的资源,以便您的应用程序将 尽可能顺利地运行

这是第一次使用线程,在我的应用程序中我可能有多达 10 个线程(类)。但是,我一次只会执行一次线程,我会违背前面的报价吗??

我希望我没有问太多问题。非常感谢您的帮助。

P.D 我在此处发布的大部分代码在 Gregor Ophey 的帮助下是不可能实现的

【问题讨论】:

J2ME 有 Future 和 Callable 吗?我要么向后移植这些概念,要么重新实现这些概念。 @DavidEhrmann 这些类不可用,我建议不要尝试编写 DIY 替代代码,除非您在并发编程方面非常有经验。 @MisterSmith 这些类的源代码可用。 【参考方案1】:

问题 #1 是关于一个与线程无关的不同问题,并且显示的代码很少。我建议您发布一个新的专门问题,并对该问题进行适当的解释。

问题 #2 和 #3:您可以像这样定义包装类:

    public class WSResult 
        private boolean success; //true if the WS call went ok, false otherwise
        private String errorMessage; //Error message to display if the WS call failed.
        private Object result; //Result, only if the WS  call succeeded.
        private boolean completed = false;

        //TODO getter and setters methods here
    

在您的屏幕中,您可以创建一个结果实例并等待它:

    WSResult result = new WSResult();

    //Start thread here
    new Thread(new LoadCustomersTask(result)).start();

    //This is old school thread sync. 
    synchronized(result)
        while(!result.isCompleted())
            result.wait();
        
    

    //Here the thread has returned, and we can diaplay the error message if any
    if(result.isSuccess())

     else 
        //Display result.getErrorMessage()
    

那么你的runnable会是这样的:

    class LoadCustomersTask implements Runnable 
        private final WSResult result;
        public LoadCustomersTask(WSResult res)
            result = res;
        

        public void run()

            //Do the WS call

            //If it went well
            result.setSuccess(true);
            result.setResult(jsonResultados);

            //Else
            result.setSuccess(false);
            result.setErrorMessage("Your error message");


            //In any case, mark as completed
            result.setcompleted(true);

            //And notify awaiting threads           
            synchronized(result)
                result.notifyAll();
                   
        
    

您也可以使用 thread.join 执行此操作,但等待/通知更好,因为您不会使屏幕依赖于 runnable 运行的特定线程。您可以在结果实例上等待/通知,如图所示,或者在 runnable 上等待/通知(如果它是供一次性使用的)。

问题 #4:是的,线程不能被滥用,特别是在 JavaME 中,程序通常在单核 CPU 中运行,频率约为 MHz。尽量不要同时运行超过 1-3 个线程。如果确实需要,请考虑使用单个线程来运行所有后台任务(阻塞队列)。

【讨论】:

拜托,你能给我解释一下这几行吗:synchronized(result)result.wait() 在我的回答中,我使用Object.wait/Object.notify 而不是Thread.sleep/Thread.interrupt 来协调线程。您可以阅读here 了解有关差异的更多信息,但想法是相同的:一个线程等待第二个线程完成。调用 wait/notify 时需要同步块,否则会抛出异常。 非常感谢@Mister Smith 我按照你的建议做了,现在我的代码更有条理了。

以上是关于J2ME - 如何使线程返回一个值,并在该线程完成后,在其他操作中使用返回值?的主要内容,如果未能解决你的问题,请参考以下文章

如何在python中启动一个线程池并在一个线程完成时停止?

J2me 使用更改的 ListModel 更新容器列表

如何使“主线程”等待“子线程”执行结束后再继续执行

命令不在 J2me 的单独线程中工作

如何使“主线程”等待“子线程”执行结束后再继续执行

如何使用 Platform.runLater 使 JavaFX 线程安全