linux社区爱心援助Linux认证系列教程业界动态站务新闻公司招聘网络学院网址大全LPI专题CISCO专题
设为首页
加入收藏
管理团队
JSP  
JAVA  
PERL  
 您的位置:首页 > 开发语言 > JAVA >
栏目导栏
  php
  JSP
  ASP
  asp.net
  JAVA
  c/c++/c#
  perl
  JavaScript
  Basic
  Delphi
资料搜索
热门文章
·Ajax实现在textbox中模糊查询显
·Ajax实现分页查询
·JAVA反编译工具jad的简单用法
·基于Spring+Hibernate+Eclip
·java定时执行的三种方法
·读取数码照片中的Exif信息
·JSP获取客户端的浏览器和操作系
·Java文件操作大全
·Java中利用通讯API编写短信软件
·JAVA技术:上传图片的缩放处理
·利用Java实现网页浏览器
·Java编程基础
·Java调用Oracle的过程和函数
·JAVA得到网卡物理地址(Windows
·NetBeans vs Eclipse之性能参数
最新文章
·实例解析:Linux操作系统下Jav
·JAVA得到网卡物理地址(Windows
·Ajax实现在textbox中模糊查询显
·Ajax简单示例之改变下拉框动态
·AJAX如何与后台交互
·Ajax实现分页查询
·Ajax核心:XMLHTTP组件相关技术
·面向Java程序员的Ajax:构建动态
·JSP获取客户端的浏览器和操作系
·提高Java技能的几种简单有效的
·敏捷开发中需掌握移除重复代码
·Java中利用通讯API编写短信软件
·关于String和StringBuffer
·用Java得到硬盘空间
·如何优化JAVA程序设计和编码,
Google
 
如何使用Java编写多线程程序
[ 作者:  加入时间:2007-10-25 11:26:17  来自:Linux联盟收集整理 ]

一、简介

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也是一样。 j97Linux联盟
§ 每个线程执行其代码的方式都是一次顺序执行的。 j97Linux联盟
§ 一个线程执行其代码是与其他线程独立开来的。如果诸线程之间又相互协作的话,就必须采用一定的交互机制。 j97Linux联盟
§ 前面已经说过,线程是共享地址空间的,如果控制不当,这里很有可能出现死锁。

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

二、准备知识

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

上锁

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

信号量

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

class Semaphore { j97Linux联盟
private int count; j97Linux联盟
public Semaphore(int count) { j97Linux联盟
this.count = count; j97Linux联盟
} j97Linux联盟
public synchronized void acquire() { j97Linux联盟
while(count == 0) { j97Linux联盟
try { j97Linux联盟
wait(); j97Linux联盟
} catch (InterruptedException e) { j97Linux联盟
//keep trying j97Linux联盟
} j97Linux联盟
} j97Linux联盟
count--; j97Linux联盟
} j97Linux联盟
public synchronized void release() { j97Linux联盟
count++; j97Linux联盟
notify(); //alert a thread that´s blocking on this semaphore j97Linux联盟
} j97Linux联盟
}

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

这里只列出几个常用的方法和属性值。 j97Linux联盟
属性值,有三个MAX_PRIORITY,MIN_PRIORITY,NORM_PRIORITY

方法:

Thread(); //建立一个线程 j97Linux联盟
void run(); //对于一个继承了Runnable接口的class而言, j97Linux联盟
//他运行一个线程,否着他什么都不做 j97Linux联盟
void setPriority(int newPriority); //设置优先级 j97Linux联盟
void start(); //运行一个程序 j97Linux联盟
void sleep(long millis); //线程睡眠millis毫秒 j97Linux联盟
static void yield(); //临时pause一个程序以便起他线程运行

三、程序示例

例一、

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

public class AutomatedTellerMachine extends Teller { j97Linux联盟
public void withdraw(float amount) { j97Linux联盟
Account a = getAccount(); j97Linux联盟
if (a.deduct(amount)) j97Linux联盟
dispense(amount); j97Linux联盟
printReceipt(); j97Linux联盟
} j97Linux联盟
} j97Linux联盟
public class Account { j97Linux联盟
private float total; j97Linux联盟
public boolean deduct(float t) { j97Linux联盟
if (t <= total) { j97Linux联盟
total -= t; j97Linux联盟
return true; j97Linux联盟
} j97Linux联盟
return false; j97Linux联盟
} j97Linux联盟
}

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

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

例二、

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

import exception.ProducerConsumerException; j97Linux联盟
/** j97Linux联盟
* Consumer.java j97Linux联盟
* Consumer j97Linux联盟
* By: Jiabo j97Linux联盟
* Date: Mar 21, 2004 j97Linux联盟
* Time: 2:47:58 PM j97Linux联盟
*/ j97Linux联盟
public class Consumer extends Thread { j97Linux联盟
private Warehouse warehouse; j97Linux联盟
private String id; j97Linux联盟
public Consumer(Warehouse warehouse, String id) { j97Linux联盟
this.warehouse = warehouse; j97Linux联盟
this.id = id; j97Linux联盟
} j97Linux联盟
public void run() { j97Linux联盟
int tmp = (int) Math.random() * 10; j97Linux联盟
try { j97Linux联盟
warehouse.get(tmp); j97Linux联盟
System.out.println("Consumer # " + this.id + " get " + tmp); j97Linux联盟
} catch (ProducerConsumerException e) { j97Linux联盟
e.printStackTrace(); j97Linux联盟
} j97Linux联盟
try { j97Linux联盟
sleep((int) (Math.random() * 100)); j97Linux联盟
} catch (InterruptedException e) { j97Linux联盟
e.printStackTrace(); j97Linux联盟
} j97Linux联盟
} j97Linux联盟
}

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

import exception.*; j97Linux联盟
/** j97Linux联盟
* Producer.java j97Linux联盟
* Producer j97Linux联盟
* By: Jiabo j97Linux联盟
* Date: Mar 21, 2004 j97Linux联盟
* Time: 2:47:45 PM j97Linux联盟
*/ j97Linux联盟
public class Producer extends Thread { j97Linux联盟
private Warehouse warehouse; j97Linux联盟
private String id; j97Linux联盟
public Producer(Warehouse warehouse, String id) { j97Linux联盟
this.warehouse = warehouse; j97Linux联盟
this.id = id; j97Linux联盟
} j97Linux联盟
public void run() { j97Linux联盟
int tmp = (int) Math.random() * 10; j97Linux联盟
if (tmp != 0) { j97Linux联盟
try { j97Linux联盟
warehouse.put(tmp); j97Linux联盟
System.out.println("Consumer # " + this.id + " put " + tmp); j97Linux联盟
} catch (ProducerConsumerException e) { j97Linux联盟
e.printStackTrace(); j97Linux联盟
} j97Linux联盟
} j97Linux联盟
try { j97Linux联盟
sleep((int) (Math.random() * 100)); j97Linux联盟
} catch (InterruptedException e) { j97Linux联盟
e.printStackTrace(); j97Linux联盟
} j97Linux联盟
} j97Linux联盟
}

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

import exception.*; j97Linux联盟
/** j97Linux联盟
* Warehouse j97Linux联盟
* By: Jiabo j97Linux联盟
* Date: Mar 21, 2004 j97Linux联盟
* Time: 2:48:10 PM j97Linux联盟
*/ j97Linux联盟
public class Warehouse { j97Linux联盟
// max capability of the warehouse j97Linux联盟
private int MAX; j97Linux联盟
private int contents; j97Linux联盟
// init with max capacity j97Linux联盟
public Warehouse(int max) { j97Linux联盟
this.MAX = max; j97Linux联盟
this.contents = 0; j97Linux联盟
} j97Linux联盟
public synchronized void get(int amount) throws ProducerConsumerException { j97Linux联盟
// the amount you want to get is bigger than the contends that the warehouse stores j97Linux联盟
if (amount > this.contents) { j97Linux联盟
throw new NotEnoughGoodsException(); j97Linux联盟
} j97Linux联盟
amount -= contents; j97Linux联盟
} j97Linux联盟
public synchronized void put(int amount) throws ProducerConsumerException { j97Linux联盟
// the amount you want to put is out of the capability of the warehouse j97Linux联盟
if (amount > (this.MAX - this.contents)) { j97Linux联盟
throw new WarehouseFullException(); j97Linux联盟
} else if (this.contents == 0) { j97Linux联盟
// warehouse is empty j97Linux联盟
throw new WarehouseEmptyException(); j97Linux联盟
} j97Linux联盟
amount += contents; j97Linux联盟
} j97Linux联盟
}

致谢:

非常感谢挚友eflyer在病中为本文的部分程序提出了宝贵建议,在此表示诚挚的谢意。

参考:

§ http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml j97Linux联盟
§ Java Threads, 2nd edition Scott Oaks & Henry Wong 2nd Edition January 1999 ISBN: 1-56592-418-5, 332 pages

Linux联盟收集整理 ,转贴请标明原始链接,如有任何疑问欢迎来本站Linux论坛讨论
评论】【加入收藏夹】【 】【打印】【关闭
※ 相关链接
 ·通过JAVAScript实现页面自适应  (2007-10-30 14:27:43)
 ·如何优化JavaScript脚本的性能  (2007-10-30 14:25:25)
 ·几个有用的Javascript验证脚本  (2007-10-30 14:24:47)
 ·Javascript+ASP打造无刷新新闻列表  (2007-10-30 14:23:38)
 ·如何用javascript防止双击  (2007-10-30 14:19:44)
 ·JavaScript 访问 JSF 组件的方法  (2007-10-30 14:18:21)
 ·JavaScript实现仿Windows关机效果  (2007-10-30 14:14:42)
 ·JavaScript去除空格的三种方法  (2007-10-30 14:07:32)
 ·用Javascript评估用户输入密码的强度  (2007-10-30 14:06:19)
 ·JavaScript处理事件:单击事件onClick  (2007-10-30 14:01:38)