Java中的线程

来源:互联网 发布:单片机lcd12864 编辑:程序博客网 时间:2024/06/05 04:29

转自 :http://tech.it168.com/a2011/0922/1250/000001250289.shtml

进程概念
  一般可以在同一时间内执行多个程序的操作系统都有进程的概念。一个进程就是一个执行中的程序,而每一个进程都有自己独立的一块内存空间、一组系统资源。在进程的概念中,每一个进程的内部数据和状态都是完全独立的。
线程概念
  多线程指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务。多线程意味着一个程序的多行语句可以看上去几乎在同一时间内同时运行。

Java中的线程
  线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制。但与进程不同的是,同类的多个线程共享一块内存空间和一组系统资源,所以系统在各个线程之间切换时,资源占用要比进程小得多,正因如此,线程也被称为轻量级进程。一个进程中可以包含多个线程。图8-4所示是计时器程序进程和线程之间的关系,主线程负责管理子线程,即子线程的启动、挂起、停止等操作。

  Java的线程类是java.lang.Thread类。当生成一个Thread类的对象之后,一个新的线程就产生了。Java中每个线程都是通过某个特定Thread对象的方法run()来完成其操作的,方法run( )称为线程体。
  下面是构建线程类几种常用的方法:
  public Thread()
  public Thread(Runnable target)
  public Thread(Runnable target, String name)
  public Thread(String name)
  参数target是一个实现Runnable接口的实例,它的作用是实现线程体的run()方法。目标target可为null,表示由本身实例来执行线程。name参数指定线程名字,但没有指定的构造方法,线程的名字是JVM分配的,例如JVM指定为thread-1、thread-2等名字。
  1、Java中的实现线程体方式1
  在Java中有两种方法实现线程体:一是继承线程类Thread,二是实现接口Runnable。下面我们先看看继承线程类Thread方式。
  如果采用第1种方式,它继承线程类Thread并重写其中的方法 run(),在初始化这个类实例的时候,目标target可为null,表示由本实例来执行线程体。由于Java只支持单重继承,用这种方法定义的类不能再继承其他父类,例如代码清单8-1,完整代码请参考chapter8_1工程中chapter8_1代码部分。

[java] view plaincopy
  1. public class chapter8_1 extends Thread {  
  2.   
  3.     boolean isRunning = true;  
  4.   
  5.     int timer = 0;  
  6.   
  7.     /** 
  8.      * 线程体代码 
  9.      */  
  10.     @Override  
  11.     public void run() {  
  12.         while (isRunning) {  
  13.             try {  
  14.                 Thread.currentThread().sleep(1000);  
  15.                 timer++;  
  16.                 System.out.println("逝去了 "+timer+" 秒");  
  17.             } catch (InterruptedException e) {  
  18.                 e.printStackTrace();  
  19.             }  
  20.         }  
  21.     }  
  22.       
  23.     public static void main(String[] args) {  
  24.   
  25.         chapter8_1 t1 = new chapter8_1();  
  26.   
  27.         t1.start();  
  28.         System.out.println("计时器启动...");  
  29.         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));  
  30.         try {  
  31.             String line = br.readLine();  
  32.             if (line.equalsIgnoreCase("1")) {  
  33.                 t1.isRunning = false;  
  34.                 /*t1.stop();*/  
  35.             }  
  36.         } catch (IOException e) {  
  37.             e.printStackTrace();  
  38.         }  
  39.     }  
  40.   
  41.     }  

在main主方法中通过new chapter8_1()创建子线程,并通过t1.start()方法启动子线程,main主方法所在线程为主线程,主线程负责管理其他的子线程。本例进程、主线程和子线程之间的关系如图8-5所示。
  子线程启动之后就开始调用run()方法,run()是一个线程体,我们在子线程中处理事情就是在这里编写代码实现的。本案例中子线程要做的事情就是:休眠1s,计时器加1,再反复执行。Thread.currentThread().sleep(1000)就是休眠1s。
  为了能够停止线程,我们在主线程中增加了一个标识,通过在控制台输入一个字符
  “1”来改变该标识t1.isRunning = false,从而结束这个线程。

注意:
  事实上线程中有一个stop()方法也可以停止线程,但是由于这种方法会产生线程死锁问题,所以在新版JDK中已经废止了,它的替代解决方法就是增加标识,就是我们在本例中采用的方案。
  很多人觉得线程难理解,主要有两个问题:
  线程休眠,既然线程已经休眠了,程序的运行速度还能提高吗?
  线程体一般都进行死循环,既然线程死循环,程序就应该死掉了,就会没有反应。
  1.关于线程休眠问题
  对线程休眠问题头痛的读者,其实还是在用单线程的思维模式考虑问题,多数情况下我们的PC都是单CPU的,某个时间点只能有一个线程运行。所谓多线程就是多个线程交替执行就好像同时运行似的。因此,休眠当前线程可以交出CPU控制权,让其他的线程有机会运行,多个线程之间只有交替运行效率才是最高的,这就像我们开车过十字路口,只有我等等,让你先过,你再等等让他先过,才能保证最高效率,否则就会造成交通系统崩溃,对线程情况也是一样的。因此,多线程中线程的休眠是程序运行的最有效方式。
  2.关于线程体死循环问题
  在单线程中如果是死循环,程序应就会死掉,没有反应,但是多线程中线程体(run方法)中的死循环,可以保证线程一直运行,如果不循环线程,则运行一次就停止了。在上面的例子中线程体运行死循环,可以保证线程一直运行,每次运行都休眠1s,然后唤醒,再然后把时间信息输出到控制台。所以,线程体死循环是保证子线程一直运行的前提。由于是子线程它不会堵塞主线程,就不会感觉到程序死掉了。但是需要注意的是有时我们确实执行一次线程体,就不需要循环了。
  程序运行后开始启动线程,线程启动后就计算逝去的时间,每过1s将结果输出到控制台。当输入1字符后线程停止,程序终止。如图8-6所示。

Java中的实现线程体方式2
  上面介绍继承Thread方式实现线程体,下面介绍另一种方式,这种方式是提供一个实现接口Runnable的类作为一个线程的目标对象,构造线程时有两个带有Runnable target参数的构造方法:
  Thread(Runnable target);
  Thread(Runnable target, String name)。
  其中的target就是线程目标对象了,它是一个实现Runnable的类,在构造Thread类时候把目标对象(实现Runnable的类)传递给这个线程实例,由该目标对象(实现Runnable的类)提供线程体run()方法。这时候实现接口Runnable的类仍然可以继承其他父类。
  请参看代码清单8-2,这是一个Java AWT的窗体应用程序,完整代码请参考chapter8_2工程中chapter8_2_1代码部分。
[java] view plaincopy
  1. public class chapter8_2_1 extends Frame implements ActionListener, Runnable {  
  2.   
  3.     private Label label;  
  4.     private Button button1;  
  5.     private Thread clockThread;  
  6.     private boolean isRunning = false;  
  7.     private int timer = 0;  
  8.   
  9.     public chapter8_2_1() {  
  10.         button1 = new Button("结束计时");  
  11.         label = new Label("计时器启动...");  
  12.         button1.addActionListener(this);  
  13.         setLayout(new BorderLayout());  
  14.         add(button1, "North");  
  15.         add(label, "Center");  
  16.         setSize(320480);  
  17.         setVisible(true);  
  18.   
  19.         clockThread = new Thread(this);  
  20.         /* 线程体是Clock对象本身,线程名字为"Clock" */  
  21.         clockThread.start(); /* 启动线程 */  
  22.         isRunning = true;  
  23.     }  
  24.   
  25.     @Override  
  26.     public void actionPerformed(ActionEvent event) {  
  27.         isRunning = false;  
  28.     }  
  29.   
  30.     @Override  
  31.   
  32.     public void run() {  
  33.         while (isRunning) {  
  34.             try {  
  35.                 Thread.currentThread().sleep(1000);  
  36.                 timer++;  
  37.                 label.setText("逝去了 " + timer + " 秒");  
  38.             } catch (InterruptedException e) {  
  39.                 e.printStackTrace();  
  40.             }  
  41.         }  
  42.     }  
  43.   
  44.     public static void main(String args[]) {  
  45.         chapter8_2_1 a = new chapter8_2_1();  
  46.     }  
  47.   
  48. }  

其中关于Java AWT知识本书就不在这里介绍了,有兴趣的读者可以自己看看相关书籍。在本例中构建AWT窗体的应用程序方式是继承Frame类。采用第1种方式——继承方式实现线程体是不可以的,因为Java是单继承的,这个类不能既继承Frame又继承Thread。应该采用第2种方式——实现Runnable接口方式。Runnable接口也有一个run()方法,它是实现线程体方法,其代码处理与上一节是一样。需要注意的是,在第2种方法中,创建了一个Thread成员变量clockThread,才用构造方法new Thread(this)创建一个线程对象,其中创建线程使用的构造方法是Thread(Runnable target),其中的this就是代表本实例,它是一个实现了Runnable接口的实现类。
  程序运行结果如图8-7所示,屏幕开始加载的时候线程启动开始计算时间,1s更新一次UI,当单击“结束计时”按钮时,停止计时。

Java中的实现线程体方式3
  实现线程体方式3是实现线程体方式2的变种,本质上还是实现线程体方式2,但是在Android应用开发中经常采用第3种方式。下面我们看第3种方式的计时器代码清单8-3,完整代码请参考chapter8_2工程中 chapter8_2_2代码部分。
  【代码清单8-3】
[java] view plaincopy
  1. public class chapter8_2_2 extends Frame implements ActionListener {  
  2.   
  3.     private Label label;  
  4.     private Button button1;  
  5.     private Thread clockThread;  
  6.   
  7.     private boolean isRunning = false;  
  8.     private int timer = 0;  
  9.   
  10.     public chapter8_2_2() {  
  11.         button1 = new Button("结束计时");  
  12.         label = new Label("计时器启动...");  
  13.         button1.addActionListener(this);  
  14.         setLayout(new BorderLayout());  
  15.         add(button1, "North");  
  16.         add(label, "Center");  
  17.         setSize(320480);  
  18.         setVisible(true);  
  19.   
  20.         /* 线程体是Clock对象本身,线程名字为"Clock" */  
  21.         clockThread = new Thread(new Runnable() {  
  22.             @Override  
  23.             public void run() {  
  24.                 while (isRunning) {  
  25.                     try {  
  26.                         Thread.currentThread().sleep(1000);  
  27.                         timer++;  
  28.                         label.setText("逝去了 " + timer + " 秒");  
  29.                     } catch (InterruptedException e) {  
  30.                         e.printStackTrace();  
  31.                     }  
  32.                 }  
  33.             }  
  34.         });  
  35.   
  36.         clockThread.start(); /* 启动线程 */  
  37.         isRunning = true;  
  38.     }  
  39.   
  40.     @Override  
  41.     public void actionPerformed(ActionEvent event) {  
  42.         isRunning = false;  
  43.     }  
  44.   
  45.     public static void main(String args[]) {  
  46.         chapter8_2_2 a = new chapter8_2_2();  
  47.     }  
  48.   
  49.     }  

与第2种方式比较,我们发现Frame类不再实现Runnable接口了,而是在实例化Thread类的时候,定义了一个实现Runnable接口的匿名内部类:
[java] view plaincopy
  1. clockThread = new Thread(new Runnable() {  
  2.             @Override  
  3.             public void run() {  
  4.                 while (isRunning) {  
  5.                     try {  
  6.                         Thread.currentThread().sleep(1000);  
  7.                         timer++;  
  8.                         label.setText("逝去了 " + timer + " 秒");  
  9.                     } catch (InterruptedException e) {  
  10.                         e.printStackTrace();  
  11.                     }  
  12.                 }  
  13.             }  
  14.     });  

0 0
原创粉丝点击