Java线程总结

fmms 12年前
     <p class="MsoNormal"><span>今天准备总结一下关于<span lang="EN-US">Java</span> 线程的问题,提到线程很容易与进程混淆,从计算机操作系统的发展来看,经历了这样的两个阶段:</span> </p>    <p class="MsoNormal"><span><span>       </span></span><span>单进程处理:最早以前的<span lang="EN-US">DOS</span> 系统就属于单进程处理,即:在同一个时间段上只能有一个程序在执行,所以在<span lang="EN-US">DOS</span> 系统中只要有病毒的出现,则立刻会有反映;</span> </p>    <p class="MsoNormal"><span><span>       </span></span><span>多进程处理:我们现在使用的<span lang="EN-US">Windows</span> 操作系统就是典型的一个多线程,所以,如果在<span lang="EN-US">windows</span> 中出现病毒了,则系统照样可以使用,通过<span lang="EN-US">Ctrl+Shift+delete</span> 可以查看<span lang="EN-US">windows</span> 系统的具体进程情况;</span> </p>    <p class="MsoNormal"><span><span>       </span></span><span>那么对于资源来讲,所有的<span lang="EN-US">IO</span> 设备、<span lang="EN-US">CPU</span> 等等只有一个,那么对于多线程的处理来讲,在<strong><span style="color:red;">同一个时间段</span> </strong>上会有多个程序运行,但是在<strong><span style="color:red;">同一个时间点</span> </strong>上只能有一个程序运行。所以我们可以发现线程是在进程的基础上进一步的划分,我们可以举个这样的例子,<span lang="EN-US">Eclipse</span> 中对<span lang="EN-US">Java</span> 的关键字的检查,是在<span lang="EN-US">Eclipse</span> 整个程序运行中检测运行的。因此进程中止了,线程也随之中止。但是线程中止了,进程可能依然会执行。我们可以这样理解,进程是一个静态的概念,一个任务或者说一个程序,一个进程里有一个主线程。</span> </p>    <p class="MsoNormal"><span><span>       </span></span><span>下面我们来看看<span lang="EN-US">Java</span> 中对线程处理机制的支持,在<span lang="EN-US">Java</span> 语言中对线程的实现有两种方恨死:一个是继承<span lang="EN-US">Thread</span> 类,另一个是实现<span lang="EN-US">Runnable</span> 接口。下面我们来分别来看看这两种实现方式:<span lang="EN-US"><span>       </span></span></span></p>    <p class="MsoNormal"><strong><span>继承<span lang="EN-US">Thread</span> 类:</span> </strong></p>    <p class="MsoNormal"><span><span>       </span></span><span>一个<span lang="EN-US">java</span> 类只要继承了<span lang="EN-US">Thread</span> 类<span lang="EN-US"><span>   </span></span>,同时覆写了本类中的<span lang="EN-US">run()</span> 方法,则就可以实现<span lang="EN-US">Java</span> 中的多线程操作了。</span> </p>    <p><span>MyThread.java</span> <span>:</span></p>    <pre class="brush:java; toolbar: true; auto-links: false;">package com.iflytek.thread;  /**  * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ public class MyThread extends Thread {   private String name;   public MyThread(String name) {   this.name = name;  }   public void run() {// 覆写run()方法   for (int i = 0; i < 10; i++) {    System.out.println("Thread运行:" + name + ",i=" + i);   }  }  }</pre>    <span>下面我们来实现上面的多线程操作类,<span lang="EN-US">MyThreadTest.java</span> :<pre class="brush:java; toolbar: true; auto-links: false;">package com.iflytek.thread;  /**  * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ public class MyThreadTest {   public static void main(String[] args) {   MyThread thread1 = new MyThread("线程A");   MyThread thread2 = new MyThread("线程B");    thread1.run();// 调用线程   thread2.run();  }   }</pre></span>    <p></p>    <p class="MsoNormal"><span>通过运行结果,我们可以发现其执行的结果非常有规律,先执行完第一个对象,再执行完第二个对象的,即没有实现交互的现象;</span> </p>    <p style="text-indent:21pt;" class="MsoNormal"><span>通过<span lang="EN-US">JDK</span> 文档可以发现,一旦我们调用<span lang="EN-US">Start()</span> 方法,则会通过<span lang="EN-US">JVM</span> 找到<span lang="EN-US">run()</span> 方法。所以当我们将上面调用的<span lang="EN-US">run()</span> 方法改为<span lang="EN-US">start()</span> 方法:</span></p>    <pre class="brush:java; toolbar: true; auto-links: false;">package com.iflytek.thread;  /**  * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ public class MyThreadTest {   public static void main(String[] args) {   MyThread thread1 = new MyThread("线程A");   MyThread thread2 = new MyThread("线程B");    thread1.start();// 调用线程   thread2.start();  }   }</pre>    <span>这时再去执行发现结果有交互的现象,所以这也值得我们思考为什么非要使用<span lang="EN-US">start()</span> 方法启动多线程呢?通过查看<span lang="EN-US">Java</span> 源码<span lang="EN-US">:<pre class="brush:java; toolbar: true; auto-links: false;">public synchronized void start() {//定义start方法          /**    * This method is not invoked for the main method thread or "system"    * group threads created/set up by the VM. Any new functionality added     * to this method in the future may have to also be added to the VM.    *    * A zero status value corresponds to state "NEW".           */          if (threadStatus != 0 || this != me)//判断线程是否已经启动              throw new IllegalThreadStateException();          group.add(this);          start0();//调用start0方法          if (stopBeforeStart) {       stop0(throwableFromStop);   }      }       private native void start0();//使用native关键字声明的方法没有方法体</pre></span></span>    <p></p>    <p class="MsoNormal"><span>说明:操作系统有很多种,<span lang="EN-US">Windows</span> 、<span lang="EN-US">Linux</span> 、<span lang="EN-US">UNIX</span> ,既然多线程操作中要进行<span lang="EN-US">CPU</span> 资源的强占,也就是说要等待<span lang="EN-US">CPU</span> 调度,那么这些调度的操作是由各个操作系统的底层实现的,所以在<span lang="EN-US">Java</span> 程序中根本就没法实现,那么此时<span lang="EN-US">Java</span> 的设计者定义了<span lang="EN-US">native</span> 关键字,使用此关键字表示可以调用操作系统的底层函数,那么这样的技术又称为<span lang="EN-US">JNI</span> 技术(<span lang="EN-US">Java Native Interface</span> ),而且,此方法在执行的时候将调用<span lang="EN-US">run</span> 方法完成,由系统默认调用的。</span> </p>    <p class="MsoNormal"><span>下面我们看看线程的状态:</span> </p>    <p><img title="7751f5b5-0065-3e0f-aa81-c8f5db51e8f3.png" border="0" alt="7751f5b5-0065-3e0f-aa81-c8f5db51e8f3.png" src="https://simg.open-open.com/show/e4b371e09575e7a6407b9ab9433a1d50.png" width="402" height="203" /></p>    <p> </p>    <p class="MsoNormal"><strong><span>实现<span lang="EN-US">Runnable</span> 接口:</span> </strong></p>    <p class="MsoNormal"><span><span>       </span></span><span>因为我们知道继承的单一继承的局限性,所以我们在开发中一个多线程的操作类很少去使用<span lang="EN-US">Thread</span> 类完成,而是通过<span lang="EN-US">Runnable</span> 接口完成。</span> </p>    <p> </p>    <p class="MsoNormal"><span>       查看源码发现<span lang="EN-US">Runnable</span> 的定义:</span></p>    <pre class="brush:java; toolbar: true; auto-links: false;"> public interface Runnable {      public abstract void run();  }</pre>    <span>所以一个类只要实现了此接口,并覆写<span lang="EN-US">run()</span> 方法<pre class="brush:java; toolbar: true; auto-links: false;">package com.iflytek.thread;  /**  *   * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ public class MyThreadByRunnable implements Runnable {   private String name;   public MyThreadByRunnable(String name) {   this.name = name;  }   public void run() {// 覆写run()方法   for (int i = 0; i < 10; i++) {    System.out.println("Thread运行:" + name + ",i=" + i);   }  } }</pre></span>    <span>有了多线程操作类下面我们需要启动多线程,但是在现在使用<span lang="EN-US">Runnable</span> 定义的子类中并没有<span lang="EN-US">start()</span> 方法,而只有<span lang="EN-US">Thread</span> 类中才有,在<span lang="EN-US">Thread</span> 类中存在以下的一个构造方法:<pre class="brush:java; toolbar: true; auto-links: false;"> public Thread(Runnable target) {  init(null, target, "Thread-" + nextThreadNum(), 0);     }</pre></span>    <span>此构造方法接受<span lang="EN-US">Runnable</span> 的子类实例,也就是说现在我们可以通过<span lang="EN-US">Thread</span> 类来启动<span lang="EN-US">Runnable</span> 实现的多线程。<pre class="brush:java; toolbar: true; auto-links: false;">package com.iflytek.thread;  /**  *   * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ public class MyThreadByRunnableTest {  public static void main(String[] args) {    MyThreadByRunnable thread1 = new MyThreadByRunnable("线程A");   MyThreadByRunnable thread2 = new MyThreadByRunnable("线程B");   new Thread(thread1).start();   new Thread(thread2).start();   }  }</pre></span>    <p></p>    <p class="MsoNormal"><span>当然上面的操作代码也属于交替的运行,所以此时程序也同样实现了多线的操作;</span> </p>    <p class="MsoNormal"><span> </span> </p>    <p class="MsoNormal"><span>下面我们来总结一下两种实现方式的区别及联系:</span> </p>    <p class="MsoNormal"><span><span>       </span></span><span>在程序的开发中只要是多线程则肯定永远以实现<span lang="EN-US">Runnable</span> 接口为正统操作,因为实现<span lang="EN-US">Runnable</span> 接口相比继承<span lang="EN-US">Thread</span> 类有如下的好处:</span> </p>    <p class="MsoListParagraph"><strong><span><span>1、<span style="line-height:normal;font-variant:normal;font-style:normal;font-size:7pt;font-weight:normal;"> </span></span></span></strong><strong><span>避免单继承的局限性,一个类可以同时实现多个接口</span> </strong></p>    <p class="MsoListParagraph"><strong><span><span>2、<span style="line-height:normal;font-variant:normal;font-style:normal;font-size:7pt;font-weight:normal;"> </span></span></span></strong><strong><span>适合于资源的共享</span> </strong></p>    <p class="MsoNormal"><span> </span> </p>    <p class="MsoNormal"><span>下面来说说关于线程的几个<span lang="EN-US">Demo</span> ;</span> </p>    <p class="MsoNormal"><span>1</span> <span>、两个线程访问同一个对象,<span lang="EN-US">ThreadSyncDemo.java</span> :</span></p>    <pre class="brush:java; toolbar: true; auto-links: false;">package com.iflytek.thread;  /**  * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ public class ThreadSyncDemo implements Runnable {  Timer timer = new Timer();   public static void main(String[] args) {   ThreadSyncDemo threadSyncDemo = new ThreadSyncDemo();   Thread thread1 = new Thread(threadSyncDemo);   Thread thread2 = new Thread(threadSyncDemo);   thread1.setName("t1");// 修改线程名称   thread2.setName("t2");   thread1.start();   thread2.start();  }   @Override  public void run() {   timer.add(Thread.currentThread().getName());  } }  class Timer {  private static int num = 0;   public void add(String name) {   num++;   try {    // 第一个线程执行到 这里时被休眠了,这是num为1,而第二个线程重新来执行时num为2,并休眠,而此时第一个线程启动了    Thread.sleep(1);   } catch (InterruptedException e) {   }   System.out.println(name + ",你是第" + num + "个使用timer的线程");  } }</pre>    <p></p>    <p class="MsoNormal"><span>运行结果:</span> </p>    <table style="border-bottom:medium none;border-left:medium none;border-collapse:collapse;border-top:medium none;border-right:medium none;" class="MsoTableGrid" border="1" cellspacing="0" cellpadding="0">     <tbody>      <tr>       <td style="padding-bottom:0cm;padding-left:5.4pt;width:426.1pt;padding-right:5.4pt;padding-top:0cm;" valign="top" width="568"> <p style="text-align:left;" class="MsoNormal" align="left"><span style="font-family:Consolas;color:black;font-size:10pt;">t1,</span> <span>你是第</span> <span style="font-family:Consolas;color:black;font-size:10pt;">2</span> <span>个使用</span> <span style="font-family:Consolas;color:black;font-size:10pt;">timer</span> <span>的线程</span> </p> <p class="MsoNormal"><span style="font-family:Consolas;color:black;font-size:10pt;">t2,</span> <span>你是第</span> <span style="font-family:Consolas;color:black;font-size:10pt;">2</span> <span>个使用</span> <span style="font-family:Consolas;color:black;font-size:10pt;">timer</span> <span>的线程</span> </p> </td>      </tr>     </tbody>    </table>    <p class="MsoNormal"><span>而如果程序这样改动一下,<span lang="EN-US">ThreadSyncDemo02.java</span> :</span></p>    <pre class="brush:java; toolbar: true; auto-links: false;">package com.iflytek.thread;  /**  * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ public class ThreadSyncDemo02 implements Runnable {  Timer02 timer = new Timer02();   public static void main(String[] args) {   ThreadSyncDemo02 threadSyncDemo = new ThreadSyncDemo02();   Thread thread1 = new Thread(threadSyncDemo);   Thread thread2 = new Thread(threadSyncDemo);   thread1.setName("t1");// 修改线程名称   thread2.setName("t2");   thread1.start();   thread2.start();  }   @Override  public void run() {   timer.add(Thread.currentThread().getName());  } }  class Timer02 {  private static int num = 0;   public synchronized void add(String name) {// 执行这个方法的过程之中,当前对象被锁定   synchronized (this) {// 这样的话,在{}中的线程执行的过程中不会被另一个线程打断,也就是说{}只能有一个线程    num++;    try {     Thread.sleep(1);// 第一个线程执行到         // 这里时被休眠了,这是num为1,而第二个线程重新来执行时num为2,并休眠,而此时第一个线程启动了    } catch (InterruptedException e) {    }    System.out.println(name + ",你是第" + num + "个使用timer的线程");   }  } }</pre>    <span>运行结果:</span>    <p></p>    <table style="border-bottom:medium none;border-left:medium none;border-collapse:collapse;border-top:medium none;border-right:medium none;" class="MsoTableGrid" border="1" cellspacing="0" cellpadding="0">     <tbody>      <tr>       <td style="padding-bottom:0cm;padding-left:5.4pt;width:426.1pt;padding-right:5.4pt;padding-top:0cm;" valign="top" width="568"> <p style="text-align:left;" class="MsoNormal" align="left"><span style="font-family:Consolas;color:black;font-size:10pt;">t1,</span> <span>你是第</span> <span style="font-family:Consolas;color:black;font-size:10pt;">1</span> <span>个使用</span> <span style="font-family:Consolas;color:black;font-size:10pt;">timer</span> <span>的线程</span> </p> <p class="MsoNormal"><span style="font-family:Consolas;color:black;font-size:10pt;">t2,</span> <span>你是第</span> <span style="font-family:Consolas;color:black;font-size:10pt;">2</span> <span>个使用</span> <span style="font-family:Consolas;color:black;font-size:10pt;">timer</span> <span>的线程</span> </p> </td>      </tr>     </tbody>    </table>    <p class="MsoNormal"><span> </span> </p>    <p class="MsoNormal"><span>2</span> <span>、死锁,<span lang="EN-US">ThreadDieDemo.java</span> :</span></p>    <pre class="brush:java; toolbar: true; auto-links: false;">package com.iflytek.thread;  /**  * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ public class ThreadDieDemo {   public static void main(String[] args) {   DeadLock lock1 = new DeadLock();   DeadLock lock2 = new DeadLock();   lock1.flag = 1;   lock2.flag = 2;   Thread thread1 = new Thread(lock1);   Thread thread2 = new Thread(lock2);   thread1.start();   thread2.start();  } }  class DeadLock implements Runnable {  public int flag = 1;  static Object o1 = new Object();  static Object o2 = new Object();   @Override  public void run() {   System.out.println("flag = " + flag);   if (flag == 1) {    synchronized (o1) {     try {      Thread.sleep(500);     } catch (InterruptedException e) {      e.printStackTrace();     }     synchronized (o2) {      System.out.println("o2");     }    }   }   if (flag == 2) {    synchronized (o2) {     try {      Thread.sleep(500);     } catch (InterruptedException e) {      e.printStackTrace();     }     synchronized (o1) {      System.out.println("o1");     }    }   }  } }</pre>    <p></p>    <p class="MsoNormal"><span>3</span> <span>、生产者和消费者问题:</span> </p>    <p class="MsoNormal"><span>首先简单说明一下<span lang="EN-US">sleep</span> 、<span lang="EN-US">wait</span> 、<span lang="EN-US">notify</span> 的区别:</span> </p>    <p class="MsoNormal"><span><span>       </span>sleep</span> <span>:<span lang="EN-US">sleep</span> 是在<span lang="EN-US">Thread</span> 中的,同时在<span lang="EN-US">sleep</span> 的时候锁还在;</span> </p>    <p class="MsoNormal"><span>        wait</span> <span>:<span lang="EN-US">wait</span> 必须是在锁住对象时才能<span lang="EN-US">wait</span> ,同时在<span lang="EN-US">wait</span> 的时候,锁就不在归那个对象所有了,而在其方法定义在<span lang="EN-US">Object</span> 中,它是让进入到此锁住对象的线程<span lang="EN-US">wait</span> ;</span> </p>    <p style="text-indent:21pt;" class="MsoNormal"><span>notify</span> <span>:与<span lang="EN-US">wait</span> 相对应,叫醒一个现在正在<span lang="EN-US">wait</span> 在我这个对象上的线程,谁现在正在我这个对象上等待,我就叫醒这个线程让他继续执行,他也是<span lang="EN-US">Object</span> 类中的方法;</span> </p>    <p style="text-indent:21pt;" class="MsoNormal"><span>ProductCustomerDemo.java</span> <span>:</span></p>    <pre class="brush:java; toolbar: true; auto-links: false;">package com.iflytek.thread;  /**  * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ public class ProductCustomerDemo {  public static void main(String[] args) {   WoToStack woToStack = new WoToStack();   Product product = new Product(woToStack);   Customer customer = new Customer(woToStack);   new Thread(product).start();   new Thread(customer).start();  } }  /**  * 消费和生产的对象  *   * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ class WoTo {  int id;   public WoTo(int id) {   this.id = id;  }   @Override  public String toString() {   return "WoTo [id=" + id + "]";  } }  class WoToStack {  int index = 0;  WoTo[] arrayWoTo = new WoTo[10];// 这里限制一下,框子最多装10个WoTo   /**   * 向框子中放WoTo   *    * @param wt   */  public synchronized void push(WoTo wt) {   // 这里用while是因为如果被打断还要执行判断,而如果是if则会直接进入下一个语句   while (index == arrayWoTo.length) {    try {     this.wait();    } catch (InterruptedException e) {     e.printStackTrace();    }   }   this.notifyAll();   arrayWoTo[index] = wt;   index++;  }   /**   * 从框子中去WoTo   *    * @return   */  public synchronized WoTo pop() {   while (index == 0) {    try {     this.wait();    } catch (InterruptedException e) {     e.printStackTrace();    }   }   this.notifyAll();   index--;   return arrayWoTo[index];  } }  /**  * 生产者  *   * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ class Product implements Runnable {  // 首先生产者需要知道生产WoTo放在哪里  WoToStack stack = null;   public Product(WoToStack stack) {   this.stack = stack;  }   @Override  public void run() {   for (int i = 0; i < 20; i++) {// 这里我们限制一下每一个生产者可以生产20个WoTo    WoTo woTo = new WoTo(i);    stack.push(woTo);    System.out.println("生产者生产了 :" + woTo);    try {     Thread.sleep((int) Math.random() * 200);    } catch (InterruptedException e) {     e.printStackTrace();    }   }  } }  class Customer implements Runnable {  WoToStack stack = null;   public Customer(WoToStack stack) {   this.stack = stack;  }   @Override  public void run() {   for (int i = 0; i < 20; i++) {// 这里我们也限制一下每一个小费者可以消费20个WoTo    WoTo woTo = stack.pop();    System.out.println("消费者消费了 :" + woTo);    try {     Thread.sleep((int) Math.random() * 1000);    } catch (InterruptedException e) {     e.printStackTrace();    }   }  } }</pre>    <p></p>    <p class="MsoNormal"><span>4</span> <span>、卖票问题(<span lang="EN-US">Runnable</span> 资源共享):</span> </p>    <p class="MsoNormal"><span>MyThread.java:</span></p>    <pre class="brush:java; toolbar: true; auto-links: false;">package com.iflytek.maipiao;  /**  * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ public class MyThread extends Thread {   private int ticket = 5;// 一共5张票   public void run() {   for (int i = 0; i < 50; i++) {    if (this.ticket > 0) {     System.out.println("卖票:ticket = " + this.ticket--);    }   }  }  }</pre>    <span>下面建三个线程对象,同时卖票,<span lang="EN-US">ThreadTicket.java</span> :<pre class="brush:java; toolbar: true; auto-links: false;">package com.iflytek.maipiao;  /**  * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ public class ThreadTicket {   public static void main(String[] args) {   MyThread thread1 = new MyThread();   MyThread thread2 = new MyThread();   MyThread thread3 = new MyThread();    // 开始卖票   thread1.start();   thread2.start();   thread3.start();  }  }</pre></span>    <p class="MsoNormal"><span>运行发现一共卖了<span lang="EN-US">15</span> 张票,但是实际上只有<span lang="EN-US">5</span> 张票,所以证明每一个线程都卖自己的票,这样就没有达到资源共享的目的。</span> </p>    <p class="MsoNormal"><span>其实我们使用<span lang="EN-US">Runnable</span> 接口的话,则就可以实现资源的共享:</span> </p>    <p class="MsoNormal"><span>MyThreadByRunnable.java</span> <span>:</span></p>    <pre class="brush:java; toolbar: true; auto-links: false;">package com.iflytek.maipiao;  /**  * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ public class MyThreadByRunnable implements Runnable {   private int ticket = 5;// 一共5张票   public void run() {   for (int i = 0; i < 50; i++) {    if (this.ticket > 0) {     System.out.println("卖票:ticket = " + this.ticket--);    }   }  }  }</pre>    <span>同样,我们再弄一个多线程进行卖票的操作,<span lang="EN-US">RunnableTicket.Java</span> :<pre class="brush:java; toolbar: true; auto-links: false;">package com.iflytek.maipiao;  /**  * @author xudongwang 2012-1-1  *   *         Email:xdwangiflytek@gmail.com  */ public class RunnableTicket {   public static void main(String[] args) {   MyThreadByRunnable threadByRunnable = new MyThreadByRunnable();   new Thread(threadByRunnable).start();   new Thread(threadByRunnable).start();   new Thread(threadByRunnable).start();  } }</pre></span>    <p></p>    <p class="MsoNormal"><span>虽然现在程序中有三个线程,但是从运行结果上看,三个线程一共卖出了<span lang="EN-US">5</span> 张票,也就是说使用<span lang="EN-US">Runnable</span> 实现的多线程可以达到资源共享的目的。</span> </p>    <p class="MsoNormal"><span>实际上,<span lang="EN-US">Runnable</span> 接口和<span lang="EN-US">Thread</span> 类之间还是存在联系的</span></p>    <pre class="brush:java; toolbar: true; auto-links: false;">Public class Thread implements Runnable {</pre>    <p></p>    <p class="MsoNormal"><span>发现<span lang="EN-US">Thread</span> 类也是<span lang="EN-US">Runnable</span> 接口的子类。</span> </p>    <p class="MsoNormal"><span>在实际的开发中比如说发多个邮件提醒等都会用到线程的,所以线程还是很重要的;<br /> <br /> 转自:<a href="/misc/goto?guid=4959500212274664494" target="_blank">http://xdwangiflytek.iteye.com/blog/1333128</a></span> </p>    <p class="MsoNormal"></p>    <p class="MsoNormal"></p>    <p></p>    <p style="text-indent:21pt;" class="MsoNormal"></p>    <p class="MsoNormal"></p>    <p class="MsoNormal"></p>    <p class="MsoNormal"></p>    <p class="MsoNormal"></p>    <p style="text-indent:21pt;" class="MsoNormal"></p>    <p></p>