📄 编写多线程的 java 应用程序——如何避免当前编程中最常见的问题.htm
字号:
src="编写多线程的 Java 应用程序——如何避免当前编程中最常见的问题.files/c.gif" width=8></TD>
<TD width=8><IMG height=1 alt=""
src="编写多线程的 Java 应用程序——如何避免当前编程中最常见的问题.files/c.gif" width=8></TD></TR>
<TR>
<TD width="100%" bgColor=#cc6633 colSpan=3 height=3><IMG height=3
alt="" src="编写多线程的 Java 应用程序——如何避免当前编程中最常见的问题.files/c.gif" width=150
border=0></TD></TR>
<TR>
<TD width="100%" bgColor=#333333 colSpan=3 height=1><IMG height=1
alt="" src="编写多线程的 Java 应用程序——如何避免当前编程中最常见的问题.files/c.gif" width=150
border=0></TD></TR>
<TR>
<TD width="100%" bgColor=#000000 colSpan=3 height=1><IMG height=1
alt="" src="编写多线程的 Java 应用程序——如何避免当前编程中最常见的问题.files/c.gif" width=150
border=0></TD></TR></TBODY></TABLE></NOBR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD vAlign=top width=5><IMG height=1 alt=""
src="编写多线程的 Java 应用程序——如何避免当前编程中最常见的问题.files/c.gif" width=5></TD>
<TD vAlign=top><BR>
<TABLE cellSpacing=0 cellPadding=2 width=137 align=right border=0>
<TBODY>
<TR vAlign=bottom align=middle>
<TD vAlign=bottom bgColor=#ffffff><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index_eng.shtml"
target=_blank><IMG height=20 alt=英文原文
src="编写多线程的 Java 应用程序——如何避免当前编程中最常见的问题.files/src.jpg" width=86
border=0></A></TD></TR></TBODY></TABLE><!-- Title --><B><FONT
class=title>编写多线程的 Java 应用程序</FONT></B><BR><B
class=subhead>如何避免当前编程中最常见的问题</B> <!-- Start Related dW Content Area --><!-- Start TOC -->
<TABLE cellSpacing=0 cellPadding=0 width=120 align=right border=0>
<TBODY>
<TR>
<TD align=middle bgColor=#cc6633><FONT
face=helvetica,helv,arial color=#ffffff
size=-1><B>内容:</B></FONT></TD></TR>
<TR>
<TD width="100%" bgColor=#000000><FONT
face=helvetica,helv,arial size=-3><IMG height=1
src="编写多线程的 Java 应用程序——如何避免当前编程中最常见的问题.files/c.gif" width=1
border=0></FONT></TD></TR>
<TR>
<TD width="100%" bgColor=#ffffff><FONT
face=helvetica,helv,arial size=-3><IMG height=4
src="编写多线程的 Java 应用程序——如何避免当前编程中最常见的问题.files/c.gif" width=1
border=0></FONT></TD></TR>
<TR>
<TD class=sidefont><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml#1">线程是什么</A></TD></TR>
<TR>
<TD class=sidefont><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml#2">线程和
Java 语言</A></TD></TR>
<TR>
<TD class=sidefont><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml#3">上锁</A></TD></TR>
<TR>
<TD class=sidefont><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml#4">信号量</A></TD></TR>
<TR>
<TD class=sidefont><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml#5">常见的上锁问题</A></TD></TR>
<TR>
<TD class=sidefont><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml#6">为不同线程模型设计系统</A></TD></TR>
<TR>
<TD class=sidefont><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml#7">线程和
AWT/Swing</A></TD></TR>
<TR>
<TD class=sidefont><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml#8">总结</A></TD></TR>
<TR>
<TD class=sidefont><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml#resources">资料</A></TD></TR>
<TR>
<TD class=sidefont><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml#author1">关于作者</A></TD></TR>
<TR>
<TD class=sidefont><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml#rating">您对这篇文章的看法</A></TD></TR>
<TR>
<TD bgColor=#000000><IMG height=5 alt=""
src="编写多线程的 Java 应用程序——如何避免当前编程中最常见的问题.files/c.gif"
width=150></TD></TR></TBODY></TABLE><!-- End TOC --><!-- START PAPER BODY --><!-- SET BODY FONT -->
<P><FONT face="Arial, sans-serif" size=-1><A
href="http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml#author1">Alex
Roetter</A> (<A
href="mailto:aroetter@CS.Stanford.edu">aroetter@CS.Stanford.edu</A>)<BR>Teton
Data Systems 的软件工程师<BR>2001 年 2 月</FONT></P>
<BLOCKQUOTE><FONT face="Arial, sans-serif" size=-1>Java Thread API
允许程序员编写具有多处理机制优点的应用程序,在后台处理任务的同时保持用户所需的交互感。Alex Roetter 介绍了 Java
Thread
API,并概述多线程可能引起的问题以及常见问题的解决方案。</FONT></BLOCKQUOTE><BR><BR><FONT
face="Arial, sans-serif" size=-1></FONT>
<P><FONT face="Arial, sans-serif" size=-1>几乎所有使用 AWT 或 Swing
编写的画图程序都需要多线程。但多线程程序会造成许多困难,刚开始编程的开发者常常会发现他们被一些问题所折磨,例如不正确的程序行为或死锁。</FONT></P>
<P><FONT face="Arial, sans-serif"
size=-1>在本文中,我们将探讨使用多线程时遇到的问题,并提出那些常见陷阱的解决方案。</FONT></P>
<P><FONT face="Arial, sans-serif" size=-1><A id=1 name=1></A><B
class=subhead>线程是什么?</B><BR>一个程序或进程能够包含多个线程,这些线程可以根据程序的代码执行相应的指令。多线程看上去似乎在并行执行它们各自的工作,就像在一台计算机上运行着多个处理机一样。在多处理机计算机上实现多线程时,它们确实<I>
可以</I>并行工作。和进程不同的是,线程共享地址空间。也就是说,多个线程能够读写相同的变量或数据结构。</FONT></P>
<P><FONT face="Arial, sans-serif"
size=-1>编写多线程程序时,你必须注意每个线程是否干扰了其他线程的工作。可以将程序看作一个办公室,如果不需要共享办公室资源或与其他人交流,所有职员就会独立并行地工作。某个职员若要和其他人交谈,当且仅当该职员在“听”且他们两说同样的语言。此外,只有在复印机空闲且处于可用状态(没有仅完成一半的复印工作,没有纸张阻塞等问题)时,职员才能够使用它。在这篇文章中你将看到,在
Java 程序中互相协作的线程就好像是在一个组织良好的机构中工作的职员。</FONT></P>
<P><FONT face="Arial, sans-serif"
size=-1>在多线程程序中,线程可以从准备就绪队列中得到,并在可获得的系统 CPU
上运行。操作系统可以将线程从处理器移到准备就绪队列或阻塞队列中,这种情况可以认为是处理器“挂起”了该线程。同样,Java 虚拟机
(JVM)
也可以控制线程的移动——在协作或抢先模型中——从准备就绪队列中将进程移到处理器中,于是该线程就可以开始执行它的程序代码。</FONT></P>
<P><FONT face="Arial, sans-serif"
size=-1><I>协作式线程</I>模型允许线程自己决定什么时候放弃处理器来等待其他的线程。程序开发员可以精确地决定某个线程何时会被其他线程挂起,允许它们与对方有效地合作。缺点在于某些恶意或是写得不好的线程会消耗所有可获得的
CPU 时间,导致其他线程“饥饿”。</FONT></P>
<P><FONT face="Arial, sans-serif"
size=-1>在<I>抢占式线程</I>模型中,操作系统可以在任何时候打断线程。通常会在它运行了一段时间(就是所谓的一个时间片)后才打断它。这样的结果自然是没有线程能够不公平地长时间霸占处理器。然而,随时可能打断线程就会给程序开发员带来其他麻烦。同样使用办公室的例子,假设某个职员抢在另一人前使用复印机,但打印工作在未完成的时候离开了,另一人接着使用复印机时,该复印机上可能就还有先前那名职员留下来的资料。抢占式线程模型要求线程正确共享资源,协作式模型却要求线程共享执行时间。由于
JVM 规范并没有特别规定线程模型,Java
开发员必须编写可在两种模型上正确运行的程序。在了解线程以及线程间通讯的一些方面之后,我们可以看到如何为这两种模型设计程序。</FONT></P>
<P><FONT face="Arial, sans-serif" size=-1><A id=2 name=2></A><B
class=subhead>线程和 Java 语言</B><BR>为了使用 Java 语言创建线程,你可以生成一个
<CODE>Thread</CODE> 类(或其子类)的对象,并给这个对象发送 <CODE>start()</CODE>
消息。(程序可以向任何一个派生自 <CODE>Runnable</CODE> 接口的类对象发送 <CODE>start()</CODE>
消息。)每个线程动作的定义包含在该线程对象的 <CODE>run()</CODE> 方法中。run 方法就相当于传统程序中的
<CODE>main()</CODE> 方法;线程会持续运行,直到 <CODE>run()</CODE>
返回为止,此时该线程便死了。</FONT></P>
<P><FONT face="Arial, sans-serif" size=-1><A id=3 name=3></A><B
class=subhead>上锁</B><BR>大多数应用程序要求线程互相通信来同步它们的动作。在 Java
程序中最简单实现同步的方法就是上锁。为了防止同时访问共享资源,线程在使用资源的前后可以给该资源上锁和开锁。假想给复印机上锁,任一时刻只有一个职员拥有钥匙。若没有钥匙就不能使用复印机。给共享变量上锁就使得
Java
线程能够快速方便地通信和同步。某个线程若给一个对象上了锁,就可以知道没有其他线程能够访问该对象。即使在抢占式模型中,其他线程也不能够访问此对象,直到上锁的线程被唤醒、完成工作并开锁。那些试图访问一个上锁对象的线程通常会进入睡眠状态,直到上锁的线程开锁。一旦锁被打开,这些睡眠进程就会被唤醒并移到准备就绪队列中。</FONT></P>
<P><FONT face="Arial, sans-serif" size=-1>在 Java 编程中,所有的对象都有锁。线程可以使用
<CODE>synchronized</CODE>
关键字来获得锁。在任一时刻对于给定的类的实例,方法或同步的代码块只能被一个线程执行。这是因为代码在执行之前要求获得对象的锁。继续我们关于复印机的比喻,为了避免复印冲突,我们可以简单地对复印资源实行同步。如同下列的代码例子,任一时刻只允许一位职员使用复印资源。通过使用方法(在
<CODE>Copier</CODE> 对象中)来修改复印机状态。这个方法就是同步方法。只有一个线程能够执行一个
<CODE>Copier</CODE> 对象中同步代码,因此那些需要使用 <CODE>Copier</CODE>
对象的职员就必须排队等候。</FONT></P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc
border=1>
<TBODY>
<TR>
<TD><PRE><CODE>class CopyMachine {
public synchronized void makeCopies(Document d, int nCopies) {
//only one thread executes this at a time
}
public void loadPaper() {
//multiple threads could access this at once!
synchronized(this) {
//only one thread accesses this at a time
//feel free to use shared resources, overwrite members, etc.
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -