关于线程是怎么一回事

解决方案 »

  1.   

    Swing线程说明Swing的原则:只有从事件派发线程才能更新Swing组件Swing中的线程有三种:初始线程、事件线程、工作线程
    初始线程:用来创建GUI组件、资源加载和启动GUI组件,当UI出现之后,初始线程就完成了使命。
    事件线程:即Swing线程、事件分派线程、AWT线程、Event Dispatch Thread。Swing所有组件的事件行为都交给这个线程进行处理,Swing线程是和Swing组件进行交互的唯一的线程,所有的绘制和图形、鼠标事件、组件事件、按钮事件和所有其他事件都发生在Swing线程。
    工作线程:这是我们自己需要运行其他任务的线程,优秀的GUI程序绝不能让界面卡死,会让用户崩溃,所以这时候需要工作线程,也可以说是在背后运行的线程。使用多线程的优势:
    1, 当有大量数据操作时,不让界面卡死;
    2, 多线程可以提高性能,多个线程同时操作,能充分利用资源线程尽量要少用,原因如下:
    1, 一个进程中的多个线程共享相同的内存地址控件,这就以为着他们可以访问相同的变量和对象,而且他们从同一个堆中分配对象,这里可能会造成线程的互相干扰。
    2, 过度使用线程可能会危及程序的性能及其可维护性。与线程相关的API:Thread、Runnable、SwingUitilities
    Thread:java提供的一个抽象类,该类里面有个一个抽象的run()方法,我们在写程序时,只要继承于这个类,实现run()方法就可以了,在程序中,如果我们想用另一个线程来执行一些动作,这要把这些动作的代码写在run()方法中,然后再调用从Thread类继承来的start()方法。如:
    public class MyThread extends Thread{
    static MyThread mt=new MyThread();
    public static void main(String args[]){
    mt.start();
    for(int i=0;i<100;i++){
    System.out.println("i=" +i);
    }
    }
    public void run(){
    for(int j=0;j<100;j++){
    System.out.println("j= "+j);
    }
    }
    }
    Thread的其他方法:sleep()、stop()
    Runnable:
    Java提供了两种机制实现多线程,可以继承Thread类,也可以通过Runnable接口实现。public class RunnableDemo implements Runnable{
    Thread thread2=new Thread(this,"2");
    public void run() {
    // TODO Auto-generated method stub
    System.out.println("线程:"+thread2.getName()+"开始运行!");
    for(char ch='A';ch<='Z';ch++){
    System.out.println(ch);
    try{
    thread2.sleep(10);
    }catch(InterruptedException e){
    System.out.println("线程2异常");
    }
    } }
    public static void main(String[] args){
    Thread thread2=new Thread(new RunnableDemo());
    thread2.start();
    for(int i=0;i<100;i++){
    System.out.println("i=" +i);
    }

    }
    }
    SwingUitilities:提供了两个方法invokeLater和invoteAndWait,他们都是使事件派发线程上的可运行对象排队。当可运行对象排在事件派发队列的队首时,就调用其run方法。其效果是允许事件派发线程调用另一个线程中的任意一个代码块。invokeandwait()方法阻塞直到runnnable执行完毕;invokelater()异步地执行runnable。invokeandwait()一般不赞成使用,因为它可能导致严重的线程死锁,对你的应用造成严重的破坏。所以,让我们把它放置一边,使用invokelater()方法。实例:
    看下面这段代码的演变过程:目的是实现一个按钮的事件;文本框(searchTF)、按钮(searchButton)、输出文本区(outputTA);lookup是一个费时查询过程;
     
    第一版本:
    private void searchButton_actionPerformed(){
    outputTA.setText("Searching for: "+searchTF.getText());
    String[] results=lookup(searchTF.getText());
        outputTA.setText("");
        for (int i = 0; i < results.length; i++) {
            String result = results[i];
            outputTA.setText(outputTA.getText() + 
                            '\n' + result);
        }
    }
    问题:由于lookup比较费时,界面看上去像卡死了
    解决办法:lookup这个非swing任务起一个工作线程,如下代码
    private void searchButton_actionPerformed(){
    outputTA.setText("Searching for: "+searchTF.getText());
    final String[][] results=new String[1][1];
    new Thread(){
    public void 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);
        }
    }
    问题:后面的结果显示将不会等到lookup执行完就提前执行了,导致了结果错误
    解决办法:是否可以把后面的结果显示纳入到工作线程呢?如下代码:
    private void searchButton_actionPerformed(){
    outputTA.setText("Searching for: "+searchTF.getText());
    final String[][] results=new String[1][1];
    new Thread(){
    public void run(){
    results[0]=lookup(searchTF.getText());
        outputTA.setText("");
        for (int i = 0; i < results[0].length; i++) {
            String result = results[0][i];
            outputTA.setText(outputTA.getText() + 
                            '\n' + result);
        }
    }
    }.start();
    }
    问题:从事件派发线程之外的线程访问Swing组件是不安全的,让Swing组件的操作在工作线程中执行,这是危险的,违背了Swing的原则(只有从事件派发线程才能更新组件),也可能导致系统的错误
    解决办法:使用SwingUitilities类的invokeLater方法让后面的显示结果起一个新的线程操作排队等待lookup执行完成由Swing线程(事件派发线程)来执行,如下代码
    private void searchButton_actionPerformed(){
    outputTA.setText("Searching for: "+searchTF.getText()); final String[][] results=new String[1][1]; new Thread(){
    public void run(){
    results[0]=lookup(searchTF.getText());
    /*下面需要更新UI,必须在Swing线程中执行,所以这里创建了一个新的线程,
    由invokeLater等待lookup执行完成之后异步执行*/
    SwingUtilities.invokeLater(new Runnable(){
    public void run(){
    outputTA.setText("");
    for(int i=0;i<results[0].length;i++){
    String result=results[0][i];
    outputTA.setText(outputTA.getText()+'\n'+result);
    }
    }
    });
    }
    }.start();
    }以上基本解决了界面卡死和线程的问题,但可以看到代码的可读性和维护性都不太好。网上还其他解决办法,叫事件驱动,有兴趣可以去看看,这个方法当然会付出时间成本的代价