Java支持多线程,可以通过继续Thread类或实现Runnable接口定义线程类,并在该类中重写run方法。

一个简单的Java线程类MyThread如下所示。

  1. package com.wt.testThread;  
  2.  
  3. public class MyThread extends Thread{  
  4.     public void run()  
  5.     {  
  6.         int i = 0;  
  7.         while (true)  
  8.         {  
  9.             i++;  
  10.             System.out.println("MyThread Processing --> "+i);  
  11.             try {  
  12.                 Thread.sleep(500);  
  13.             } catch (InterruptedException e) {}  
  14.         }  
  15.     }  
  16.       
  17.     public static void main(String args[])  
  18.     {  
  19.         MyThread thread = new MyThread();  
  20.         thread.start();  
  21.     }  
  22. }  
在MyThread的run方法中,每隔500ms(Thread.sleep(500);)输出一次信息。在主函数中,新建MyThread实例对象,并使用对象的start方法启动该线程实例。程序输出结果部分如下所示。

MyThread Processing --> 1
MyThread Processing --> 2
MyThread Processing --> 3
……
 
 
多线程的同步

假设现在有两件工具,三个工人,每个工人工作时需要同时使用这两件工具。使用Java多线程模拟上述情景,使用线程类WorkThread表示工人,使用ToolBox表示两件工具所在的工具箱,如下所示。

  1. class ToolBox{  
  2.     //t1Owner,t2Owner分别表示正在使用该工具的工人名  
  3.     private String t1Owner;  
  4.     private String t2Owner;  
  5.       
  6.     //useTool表示某位工人取得工具并使用  
  7.     public void useTool(String workerName) throws InterruptedException  
  8.     {  
  9.         t1Owner = workerName;  
  10.         Thread.sleep(100);  
  11.         t2Owner = workerName;  
  12.         Thread.sleep(100);        
  13.         checkTool();  
  14.     }  
  15.       
  16.     //useTool检查工具是否正在被同一个工人使用  
  17.     public void checkTool()  
  18.     {  
  19.         if (!t1Owner.equals(t2Owner)){  
  20.             System.out.println("*Wrong*: Tool1:"+t1Owner+"\t Tool2:"+t2Owner);  
  21.         }  
  22.         else{  
  23.             System.out.println("*Right*: Tool1:"+t1Owner+"\t Tool2:"+t2Owner);  
  24.         }  
  25.     }  
  26. }  
  27.  
  28. public class WorkerThread extends Thread{  
  29.     private ToolBox toolBox;  
  30.     private String workerName;  
  31.       
  32.     public WorkerThread(String workerName,ToolBox toolBox)  
  33.     {  
  34.         super();  
  35.         this.workerName = workerName;  
  36.         this.toolBox = toolBox;  
  37.     }  
  38.       
  39.     public void run()  
  40.     {  
  41.         //工人每次休息1秒,然后使用工具工作  
  42.         while(true)  
  43.         {  
  44.             try {  
  45.                 Thread.sleep(1000);  
  46.                 toolBox.useTool(workerName);  
  47.             } catch (InterruptedException e) {}  
  48.         }  
  49.     }  
  50. }  
主函数如下所示,其中ToolBox类实例toolBox表示工人共用的工具箱,通过WorkerThread类的构造函数构造三个工人线程类,并传入工人姓名和toolBox,启动线程。
  1. public static void main(String args[])  
  2. {  
  3.     //工人共用的工具箱  
  4.     ToolBox toolBox = new ToolBox();  
  5.     //三个工人开始工作  
  6.     new WorkerThread("Tom",toolBox).start();  
  7.     new WorkerThread("Jack",toolBox).start();  
  8.     new WorkerThread("Bob",toolBox).start();          
  9. }  

 执行结果部分如下所示。

*Right*: Tool1:Bob   Tool2:Bob
*Right*: Tool1:Bob   Tool2:Bob
*Wrong*: Tool1:Bob   Tool2:Jack
*Wrong*: Tool1:Bob   Tool2:Jack
*Wrong*: Tool1:Bob   Tool2:Jack
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Jack Tool2:Jack
*Wrong*: Tool1:Jack Tool2:Tom
*Wrong*: Tool1:Jack Tool2:Tom
*Wrong*: Tool1:Jack Tool2:Tom
*Right*: Tool1:Tom   Tool2:Tom

可以看出,由于三个工人线程类共享使用工具箱中的工具,导致存在对于工具使用的竞争。由于ToolBox中useTool方法的代码(即工人使用工具)存在上述多线程之间的资源竞争,因此将其称为临界区(Critical Section)。临界区如下所示。

  1. t1Owner = workerName;  
  2. Thread.sleep(100);  
  3. t2Owner = workerName;  
  4. Thread.sleep(100);  
  5. checkTool(); 

对于上述临界区中的代码,需要实现同步,即在一个线程中,要么都执行,要么都不执行。在Java中,可以使用关键字synchronized实现同步。synchronized的使用方法有两种:

1)对于需要同步的共享实例对象,将synchronized添加为方法的修饰符,如下所示。

  1. public synchronized void useTool(String workerName) throws InterruptedException 

2)将需要同步的临界区代码放入synchronized(this){}中,如下所示。

  1. synchronized(this)  
  2. {  
  3.     t1Owner = workerName;  
  4.     Thread.sleep(100);  
  5.     t2Owner = workerName;  
  6.     Thread.sleep(100);        
  7.     checkTool();  
  8. }  

使用synchronized同步后,重新执行原主函数,得到正确执行结果。

*Right*: Tool1:Tom   Tool2:Tom
*Right*: Tool1:Bob   Tool2:Bob
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Tom   Tool2:Tom
*Right*: Tool1:Bob   Tool2:Bob
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Tom   Tool2:Tom
*Right*: Tool1:Bob   Tool2:Bob
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Tom   Tool2:Tom
*Right*: Tool1:Bob   Tool2:Bob
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Tom   Tool2:Tom
*Right*: Tool1:Bob   Tool2:Bob
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Tom   Tool2:Tom