如何使用Java编写多线程程序

 【IT168 技术文章】

         一、简介

  1、什么是线程

  要说线程,就必须先说说进程,进程就是程序的运行时的一个实例。线程呢可以看作单独地占有CPU时间来执行相应的代码的。对早期的计算机(如DOS)而言,线程既是进程,进程既是进程,因为她是单线程的。当然一个程序可以是多线程的,多线程的各个线程看上去像是并行地独自完成各自的工作,就像一台一台计算机上运行着多个处理机一样。在多处理机计算机上实现多线程时,它们确实可以并行工作,而且采用适当的分时策略可以大大提高程序运行的效率。但是二者还是有较大的不同的,线程是共享地址空间的,也就是说多线程可以同时读取相同的地址空间,并且利用这个空间进行交换数据。

  2、为什么要使用线程

  为什么要使用多线程呢?学过《计算机体系结构》的人都知道。将顺序执行程序和采用多线程并行执行程序相比,效率是可以大大地提高的。比如,有五个线程thread1, thread2, thread3, thread4, thread5,所耗的CPU时间分别为4,5,1,2,7。(假设CPU轮换周期为4个CPU时间,而且线程之间是彼此独立的)顺序执行需要花费19个CPU时间,而并行需要的时间肯定少于19个CPU时间,至于具体多少时间要看那些线程是可以同时执行的。这是在非常小规模的情况下,要是面对大规模的进程之间的交互的话,效率可以表现得更高。

  3、java中是如何实现多线程的

  与其他语言不一样的是,线程的观念在java是语言中是重要的,根深蒂固的,因为在java语言中的线程系统是java语言自建的, java中有专门的支持多线程的API库,所以你可以以最快的速度写一个支持线程的程序。在使用java创建线程的时候,你可以生成一个Thread类或者他的子类对象,并给这个对象发送start()消息(程序可以向任何一个派生自 Runnable 接口的类对象发送 start() 消息的),这样一来程序会一直执行,直到run返回为止,此时该线程就死掉了。

  在java语言中,线程有如下特点:

   在一个程序中而言,主线程的执行位置就是main。而其他线程执行的位置,程序员是可以自定义的。值得注意的是对Applet也是一样。

  每个线程执行其代码的方式都是一次顺序执行的。

  一个线程执行其代码是与其他线程独立开来的。如果诸线程之间又相互协作的话,就必须采用一定的交互机制。

  前面已经说过,线程是共享地址空间的,如果控制不当,这里很有可能出现死锁。

  各线程之间是相互独立的,那么本地变量对一个线程而言就是完全独立,私有的。所以呢,线程执行时,每个线程都有各自的本地变量拷贝。对象变量(instance variable)在线程之间是可以共享的,这也就是为什么在java中共享数据对象是如此的好用,但是java线程不能够武断地访问对象变量:他们是需要访问数据对象的权限的。

  二、准备知识

  在分析这个例子之前,然我们先看看关于线程的几个概念,上锁,信号量,和java所提供的API。

  上锁

  对于大多数的程序而言,他们都需要线程之间相互的通讯来完成整个线程的生命周期,二实现线程之间同步的最简单的办法就是上锁。为了防止相互关联的两个线程之间错误地访问共享资源,线程需要在访问资源的时候上锁和解锁,对于锁而言,有读锁,写锁和读写锁等不同的同步策略。在java中,所有的对象都有锁;线程只需要使用synchronized关键字就可以获得锁。在任一时刻对于给定的类的实例,方法或同步的代码块只能被一个线程执行。这是因为代码在执行之前要求获得对象的锁。

  信号量

  通常情况下,多个线程所访问为数不多的资源,那怎么控制呢?一个比较非常经典而起非常简单的办法就是采用信号量机制。信号量机制的含义就是定义一个信号量,也就是说能够提供的连接数;当有一个线程占用了一个连接时,信号量就减一;当一个线程是放了连接时,信号量就加一。采用这种方法就可以简单有效地控制线程的同步问题,而且实现起来也特别方便。看下面的代码:

1  class  Semaphore {

3     private   int  count;

5     public  Semaphore( int  count) {

7     this .count  =  count;

9    }
10 
11     public   synchronized   void  acquire() {
12 
13     while (count  ==   0 ) {
14 
15     try  {
16 
17    wait();
18 
19    }  catch  (InterruptedException e) {
20 
21     // keep trying
22 
23    }
24 
25    }
26 
27    count -- ;
28 
29    }
30 
31     public   synchronized   void  release() {
32 
33    count ++ ;
34 
35    notify();  // alert a thread that´s blocking on this semaphore
36 
37    }
38 
39    }
40 
41

  java中提供了哪些api以编写多线程程序

  这里只列出几个常用的方法和属性值。

  属性值,有三个MAX_PRIORITY,MIN_PRIORITY,NORM_PRIORITY

  方法:

1     Thread();  // 建立一个线程

3     void  run();  // 对于一个继承了Runnable接口的class而言,

5     // 他运行一个线程,否着他什么都不做

7     void  setPriority( int  newPriority);  // 设置优先级

9     void  start();  // 运行一个程序
10 
11     void  sleep( long  millis);  // 线程睡眠millis毫秒
12 
13     static   void  yield();  // 临时pause一个程序以便起他线程运行
14 
15

  三、程序示例

  例一、

  让我们看看下面的例子。取钱的流程是输入密码,然后确定要取得金额,如果所取的金额小于或等于可以取出的金额,WITHDRAW则返回TRUE,然后ATM机出钱,然后打印清单;否则返回FALSE,然后打印清单。如下图:

1  public   class  AutomatedTellerMachine  extends  Teller {

3     public   void  withdraw( float  amount) {

5    Account a  =  getAccount();

7     if  (a.deduct(amount))

9    dispense(amount);
10 
11    printReceipt();
12 
13    }
14 
15    }
16 
17     public   class  Account {
18 
19     private   float  total;
20 
21     public   boolean  deduct( float  t) {
22 
23     if  (t  <=  total) {
24 
25    total  -=  t;
26 
27     return   true ;
28 
29    }
30 
31     return   false ;
32 
33    }
34 
35    }
36 
37

  就这个例子而言,假设有这种情况,对同一个账号可以在不同的地方取钱,在同一时间,不同地点,妻子和丈夫取钱,妻子输入了账号上的最大金额,丈夫也是一样,假如妻子输入后已经得到true的返回值,但是丈夫的线程所得到的值还没有更新,这样丈夫也能够得到true的返回值,这样就出现了问题!这个问题怎么解决呢?在java里面提供了控制机制以保证deduct操作时的原子性,那就是关键字synchronized。

  在Account的deduct方法加入synchronized就可以解决这个问题。

  例二、

  在这里我们用多线程中最典型的例子,生产者与消费者问题。在这个例子里面我们定义了生产者Producer,消费者Consumer和仓库Warehouse三个类,在整个程序的生命周期里,生产者随机地制造出产品放到仓库中,消费者也是随即地从仓库中取出产品。

1  import  exception.ProducerConsumerException;

3     /**

5    * Consumer.java

7    * Consumer

9    * By: Jiabo
10 
11    * Date: Mar 21, 2004
12 
13    * Time: 2:47:58 PM
14 
15     */
16 
17     public   class  Consumer  extends  Thread {
18 
19     private  Warehouse warehouse;
20 
21     private  String id;
22 
23     public  Consumer(Warehouse warehouse, String id) {
24 
25     this .warehouse  =  warehouse;
26 
27     this .id  =  id;
28 
29    }
30 
31     public   void  run() {
32 
33     int  tmp  =  ( int ) Math.random()  *   10 ;
34 
35     try  {
36 
37    warehouse.get(tmp);
38 
39    System.out.println( " Consumer #  "   +   this .id  +   "  get  "   +  tmp);
40 
41    }  catch  (ProducerConsumerException e) {
42 
43    e.printStackTrace();
44 
45    }
46 
47     try  {
48 
49    sleep(( int ) (Math.random()  *   100 ));
50 
51    }  catch  (InterruptedException e) {
52 
53    e.printStackTrace();
54 
55    }
56 
57    }
58 
59    }
60 
61

  在这个类中,值得注意的一点是run方法中必须使用try-catch,因为,消费者从仓库中取东西时有可能诸如仓库中的储量不够得异常,在消费者里面也是一样,只不过异常变为仓库已满。

1  import  exception. * ;

3     /**

5    * Producer.java

7    * Producer

9    * By: Jiabo
10 
11    * Date: Mar 21, 2004
12 
13    * Time: 2:47:45 PM
14 
15     */
16 
17     public   class  Producer  extends  Thread {
18 
19     private  Warehouse warehouse;
20 
21     private  String id;
22 
23     public  Producer(Warehouse warehouse, String id) {
24 
25     this .warehouse  =  warehouse;
26 
27     this .id  =  id;
28 
29    }
30 
31     public   void  run() {
32 
33     int  tmp  =  ( int ) Math.random()  *   10 ;
34 
35     if  (tmp  !=   0 ) {
36 
37     try  {
38 
39    warehouse.put(tmp);
40 
41    System.out.println( " Consumer #  "   +   this .id  +   "  put  "   +  tmp);
42 
43    }  catch  (ProducerConsumerException e) {
44 
45    e.printStackTrace();
46 
47    }
48 
49    }
50 
51     try  {
52 
53    sleep(( int ) (Math.random()  *   100 ));
54 
55    }  catch  (InterruptedException e) {
56 
57    e.printStackTrace();
58 
59    }
60 
61    }
62 
63    }
64 
65

  最重要的一部分在Warehouse类,如上所说为了保证get何set的原子性,在这里使用了synchronized关键字,并且在操作时抛出了可能跑出的异常。

1  import  exception. * ; 

3  /**  
4  * Warehouse 
5  * By: Jiabo 
6  * Date: Mar 21, 2004 
7  * Time: 2:48:10 PM 
8  */  
9  public   class  Warehouse { 
10 
11  //  max capability of the warehouse 
12  private   int  MAX; 
13  private   int  contents; 
14 
15  //  init with max capacity 
16  public  Warehouse( int  max) { 
17  this .MAX  =  max; 
18  this .contents  =   0 ; 
19  } 
20 
21  public   synchronized   void  get( int  amount)  throws  ProducerConsumerException { 
22 
23  //  the amount you want to get is bigger than the contends that the warehouse stores 
24  if  (amount  >   this .contents) { 
25  throw   new  NotEnoughGoodsException(); 
26  } 
27 
28  amount  -=  contents; 
29  } 
30 
31  public   synchronized   void  put( int  amount)  throws  ProducerConsumerException { 
32 
33  //  the amount you want to put is out of the capability of the warehouse 
34  if  (amount  >  ( this .MAX  -   this .contents)) { 
35  throw   new  WarehouseFullException(); 
36  }  else   if  ( this .contents  ==   0 ) { 
37  //  warehouse is empty 
38  throw   new  WarehouseEmptyException(); 
39  } 
40 
41  amount  +=  contents; 
42  } 
43  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值