要修正这些问题,让我们把查找操作移入非Swing线程中。我们第一个想到的就是让整个方法在一个新的线程中执行。这样作的问题是Swing组件,本例中的文本区域,只能从Swing线程中进行编辑。下面是修改后的searchButton_actionPerformed方法: privatevoid searchButton_actionPerformed() { outputTA.setText("Searching for: " + searchTF.getText()); //the String[][] is used to allow access to // setting the results from an inner class final String[][] results = new String[1][1]; new Thread(){ publicvoid run() { results[0] = lookup(searchTF.getText()); } }.start(); outputTA.setText(""); for (int i = 0; i < results[0].length; i++) { String result = results[0][i]; outputTA.setText(outputTA.getText() + '\n' + result); } }
这种方法有很多问题。注意final String[][] 。这是一个处理匿名内部类和作用域的不得已的替代。基本上,在匿名内部类中使用的,但在外部环绕类作用域中定义的任何变量都需要定义为final。你可以通过创建一个数组来持有变量解决这个问题。这样的话,你可以创建数组为final的,修改数组中的元素,而不是数组的引用自身。既然我们已经解决这个问题,让我们进入真正的问题所在吧。图3显示了这段代码运行时发生的情况: 图 3. 在Swing线程外部进行查找 界面显示了一个null,因为显示代码在查找代码完成前被处理了。这是因为一旦新的线程启动了,代码块继续执行,而不是等待线程执行完毕。这是那些奇怪的并发代码块中的一个,下面将把它编写到一个方法中使其能够真正执行。 在SwingUtilities类中有两个方法可以帮助我们解决这些问题:invokerLater()和invokeAndWait()。每一个方法都以一个Runnable作为参数,并在Swing线程中执行它。invokeAndWait()方法阻塞直到Runnnable执行完毕;invokeLater()异步地执行Runnable。invokeAndWait()一般不赞成使用,因为它可能导致严重的线程死锁,对你的应用造成严重的破坏。所以,让我们把它放置一边,使用invokeLater()方法。 要修正最后一个变量变量scooping和执行顺序的问题,我们必须将文本区域的getText()和setText()方法调用移入一个Runnable,只有在查询结果返回后再执行它,并且在Swing线程中执行。我们可以这样作,创建一个匿名Runnable传递给invokeLater(),包括在新线程的Runnable后的文本区域操作。这保证了Swing代码不会在查找结束之前执行。下面是修正后的代码: privatevoid searchButton_actionPerformed() { outputTA.setText("Searching for: " + searchTF.getText()); final String[][] results = new String[1][1]; new Thread() { publicvoid run() { //get results. results[0] = lookup(searchTF.getText()); // send runnable to the Swing thread // the runnable is queued after the // results are returned SwingUtilities.invokeLater( new Runnable() { publicvoid run() { // Now we're in the Swing thread outputTA.setText(""); for (int i = 0; i < results[0].length; i++) { String result = results[0][i]; outputTA.setText( outputTA.getText() + '\n' + result); } } } ); } }.start(); }