`
wuhua
  • 浏览: 2111273 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

复习 Java 多线程编程

    博客分类:
  • J2SE
阅读更多

 

 

很多时候程序需要用到多线程编程。学习固然重要,但是大多数时候,只有实战才可以提高自己的编程能力。3GEYE+ph8` lO
所以要经常复习旧知识。3GEYEB7\I}m$R

'kk;O"B$e!}\3

一、简介

1、什么是线程3GEYE.O)O`'e{&Ve9`z0O

要说线程,就必须先说说进程,进程就是程序的运行时的一个实例。线程呢可以看作单独地占有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也是一样。3GEYE-~s h3Qd0X p
  § 每个线程执行其代码的方式都是一次顺序执行的。3GEYE1Nj{D5p mg
  § 一个线程执行其代码是与其他线程独立开来的。如果诸线程之间又相互协作的话,就必须采用一定的交互机制。3GEYE'B2N&]u.o
  § 前面已经说过,线程是共享地址空间的,如果控制不当,这里很有可能出现死锁。

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

 
6v�h-j7L+]v u$bQ3

二、准备知识

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

上锁

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

信号量

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

class Semaphore {3GEYEp+d2jv xf�e%u
private int count;
0{;\-Je Q3public Semaphore(int count) {3GEYEv&RmzLa�mZ
this.count = count;
Whs)D+bhf3}3GEYEA7FG2| SD `Ge
3GEYEkc v{v,N"x$o
public synchronized void acquire() {3GEYE J~Ri:D m0E ?
while(count == 0) {
(G{y jm8a5b3try {
2k c0mpa XvAK3Q0h3wait();
1?BGAN3} catch (InterruptedException e) {3GEYE"n&EL!e-G&YA�u
//keep trying
Z,W&x GO*t3}3GEYEj8s:E�~T-j7}m
}3GEYEM%BnM$|5G6j\J
count--;
d(u$})k,g3}
rjg N.^Ze0u%t/s3
1v3~s4gA3public synchronized void release() {3GEYEJ6^&kc2oi-T.Z*o
count++;
zX2X8?o+x3notify(); //alert a thread that´s blocking on this semaphore3GEYE/C9bK2tN
}3GEYE'd{0XS H
}

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

  这里只列出几个常用的方法和属性值。3GEYE!lV:D nqKO5@U*}
  属性值,有三个MAX_PRIORITY,MIN_PRIORITY,NORM_PRIORITY

  方法:

Thread(); //建立一个线程3GEYE;_.X"Q*IW6s6X�A
void run(); //对于一个继承了Runnable接口的class而言,3GEYE&f#_@.^'g$X7\a
//他运行一个线程,否着他什么都不做
+J^t [6@S:i9pq3void setPriority(int newPriority); //设置优先级
v'mq{p5g.W&M3void start(); //运行一个程序
2?;vmsDr#UL6K3void sleep(long millis); //线程睡眠millis毫秒
)r(g$fio3static void yield(); //临时pause一个程序以便起他线程运行


J['p}vL&Z;J"k&g3

三、程序示例

  例一、

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

public class AutomatedTellerMachine extends Teller {3GEYE4E5A trl5]$Ox"w1w
public void withdraw(float amount) {
)S1jf0Qe.N#jQ3Account a = getAccount();
*@y2}!| P\ _3if (a.deduct(amount))3GEYEz5ah+A%r%x5C
dispense(amount);3GEYE\ Jj Z*i ?-S]4j0[P
printReceipt();3GEYEE x {3R�c2El.]
}3GEYE| Eum#}H
}
1F~8D5H(O8E5Jl3
7dIh$A$Z&kl7C2}~3public class Account {
b+g�M5M$`k8Z(Ps3private float total;3GEYE]"P5d3SA�pH{
public boolean deduct(float t) {3GEYE*b+^E#|1mc4s
if (t <= total) {
9}:~0L~(LqzHY3total -= t;3GEYE \ `OB1@9Hj|
return true;
j"M BwF8I.yZf3}
Sq$l,L&e9FOQ3return false;
(_/_.T(o r7W3}3GEYEnJX6b Qx0O.o
}

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

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

  例二、

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

import exception.ProducerConsumerException;
+Ef#eO:r NW;`3
?R6~'H;O3/**
*_|5xUkZ6si1B3* Consumer.java3GEYE;h J%zGE1fX
* Consumer3GEYEmS$bVSQ
* By: Jiabo3GEYE&o4i0k_:D+I'M9m c7q+f;p
* Date: Mar 21, 2004
-E6~1i Y s7CT3* Time: 2:47:58 PM
;E8c;~&J&yf^3*/3GEYE~C*V)o8MysB
public class Consumer extends Thread {
{'d7Wc4a:b@3k d3
S|#B v!M3p^3private Warehouse warehouse;3GEYE$ZO9Q*co ZGt
private String id;
'I)c%V ki/DN;d3
#D,B7BU1yr6P M3public Consumer(Warehouse warehouse, String id) {3GEYE:Z4hh%qSQ M;j
this.warehouse = warehouse;3GEYEUi)_/]$IYP
this.id = id;3GEYE*H-b-?0hb%K%u
}3GEYE{mHfk
3GEYEc]#Y#Z%m kkn9Q
public void run() {3GEYE`[ j T/\1@?o

5\5y�o"q N'M~| WJ W3M3int tmp = (int) Math.random() * 10;
k*G/K(iN0F^3b+v33GEYE j*Z+KB*uGT
try {
'{e v%zn iDb3warehouse.get(tmp);
R:Drj)Y?&F(F.C7q3System.out.println("Consumer # " + this.id + " get " + tmp);
S7I`/vVUU3} catch (ProducerConsumerException e) {
Mw j2[8M%h Q3e.printStackTrace();
DfN9qXM3}3GEYE#co/t.p9] ] h#I

ZS'\({x|C%j ]8@3try {3GEYE|x)hI&h kE xd)O
sleep((int) (Math.random() * 100));3GEYE;P.m d]9J4O}!Pt
} catch (InterruptedException e) {
6i R3z#? QU-MQ s3e.printStackTrace();
]1e[1c-c3}
%s["Pyi0e8W3}
&v2d-YQ!\,S3}

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

import exception.*;3GEYE%P)[-x(o*R�bn4h4mD }

,Bt'cSD*HPr/~x3/**
XmNL1[J:aO3* Producer.java3GEYE7@$ApO {�YY
* Producer3GEYEg)nO+T[;K X\d]
* By: Jiabo
]1m].w:n1s;a#`3* Date: Mar 21, 20043GEYEymEK3b*V%E
* Time: 2:47:45 PM
hY A9J*i`B3*/3GEYE7Rr*CjY'aP
public class Producer extends Thread {
\#pV O(}:JMw%Z33GEYE/ymf]@)\j
private Warehouse warehouse;
q1p b*B7S9b6m3private String id;
%l;sC:io9b(c,\3
&L G+{K.T+D3public Producer(Warehouse warehouse, String id) {3GEYE)hq\&gyG
this.warehouse = warehouse;
,f*@d/} B3A1Ddb3this.id = id;
yqD Z:h2h3}3GEYE.m Bu4i8fU2~"n
3GEYE8U$KV|pcW
public void run() {3GEYEs:jyG]9~h:x~
3GEYE,`-Gu-Nz uIY
int tmp = (int) Math.random() * 10;
2p'g.\)z^D3Mf!n rT33GEYE8Ws \ln @]/t g A C
if (tmp != 0) {3GEYEz3]V]G5j;v'iv
try {3GEYE T{3pi~S7O7Z
warehouse.put(tmp);
)cA3T_` h!M3System.out.println("Consumer # " + this.id + " put " + tmp);3GEYE2P~+i)Uo%p
} catch (ProducerConsumerException e) {
?hL_"zxn,H%cE y*F3e.printStackTrace();3GEYE%l A,lMz*WJ
}3GEYE4L"eE;z.w(iyd5f
}
D.jc&d j0^c+u*\33GEYEr`kvo�{
try {
)yEAMt{H9C3sleep((int) (Math.random() * 100));
#Mf'Y+|j ~WK t3} catch (InterruptedException e) {
M6y9E!]5A ff$oi3e.printStackTrace();
@ @"AW L%a3}
Ls1Worh&y4}3}3GEYE S![/n'k A A
}

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

import exception.*;
0Nh2@+w$L�S33GEYE-m#w/OTf;@e u
/**3GEYE9\VeF7q4Tqw
* Warehouse
\ r6Zd ~3z gJ Y P3* By: Jiabo
0w5l7Z@%H;t9[W vz"C3* Date: Mar 21, 20043GEYE9X*\1UE|Y+JrW
* Time: 2:48:10 PM
p%S0el(gyML H3*/
5X9OTx[ T4X3public class Warehouse {3GEYE�a�f!of F

$Rd)PM"uV$s `Fy3// max capability of the warehouse
n$NCXA1mU3private int MAX;
5k|~ `_k.d#J3private int contents;
|1c,G Hj(p9X3L5{33GEYE.S1X/leg2eQ
// init with max capacity3GEYE}"P `n/P1U
public Warehouse(int max) {
%?0`(R[?Zrn�UB3this.MAX = max;3GEYEd)bA(~[ wT1G i
this.contents = 0;
2@%d*l1A9_ |3}
w2fUALX33GEYEVjG0? a\P
public synchronized void get(int amount) throws ProducerConsumerException {
YFM9F"nBn7XN33GEYExoN6N*L,y8n
// the amount you want to get is bigger than the contends that the warehouse stores3GEYE~.[ xGQ'I8j
if (amount > this.contents) {
]mn2W&e2D3mO"U3throw new NotEnoughGoodsException();
"MZKU ~1[d�}3}3GEYExt3FbrW-X6H)\X

] UX7t3d3H3amount -= contents;
~df"fs[[(^0N3}
gR(~#x/|6}ro3
-qi�E4p4z$H4P3public synchronized void put(int amount) throws ProducerConsumerException {
0YA)a*Yc'k7Xo33GEYEts+Z)R"z vG,\fH
// the amount you want to put is out of the capability of the warehouse
n3v.l�nR_)g3if (amount > (this.MAX - this.contents)) {3GEYE |:BB2GX-dJ9LN
throw new WarehouseFullException();3GEYE;i4nguWeJi2x-b
} else if (this.contents == 0) {3GEYE4T&LP5n#T%AWA7o
// warehouse is empty3GEYE`ig4i X+z�oRC$X
throw new WarehouseEmptyException();
*W!h2]7lw\3}
;MD'IBNOrU7rG.w:M3
#p)B} t.h]Z\U3amount += contents;3GEYE!rO3@R|2Ay1l!|
}
)ub,h&}VS(Co1v3}

致谢:

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

参考:

§http://www-900.ibm.com/developerWorks/cn/java/j-thread/index.shtml
2o5VS$K)m,~5x3§ Java Threads, 2nd edition Scott Oaks & Henry Wong 2nd Edition January 1999 ISBN: 1-56592-418-5, 332 pages

http://www.3geye.net/?3/viewspace-3280

分享到:
评论

相关推荐

    实验八:Java多线程

    本实验不仅实现了预期的功能,还提供了实践机会,帮助理解和掌握Java多线程编程的关键技术和注意事项。通过实验,我们可以更加深刻地理解多线程编程的优势与挑战,为后续学习高级并发编程打下坚实的基础。

    43道Java多线程高频题整理(附答案背诵版).md

    这份《43道Java多线程高频题整理(附答案背诵版)》资源是一份专门针对Java多线程编程的高频考题集,旨在帮助Java开发者系统掌握多线程的核心概念和实际应用。此资源涵盖了面试和工作中常见的多线程问题,通过详细的...

    Java网络编程期末考试复习题库+答案

    3. **多线程**: 在网络编程中,多线程技术常用于处理并发连接。Java的Thread类和Runnable接口是实现多线程的核心,线程池(ExecutorService)则可以有效管理线程,提高系统性能。 4. **网络编程**: 这包括TCP和UDP...

    Java编程复习代码

    3. **多线程(Thread)**:Java提供内置支持进行并发编程,复习内容可能包括线程的创建(通过继承`Thread`类或实现`Runnable`接口)、线程同步(如`synchronized`关键字、`wait()`, `notify()`和`notifyAll()`方法)、...

    java高级编程复习题.docx

    总的来说,这些题目涵盖了Java多线程的实现、GUI界面设计与事件处理、数据库操作(JDBC)以及基本的文件操作。在实际编程中,这些技能都是Java开发者必备的。为了完成这些任务,开发者需要熟练掌握Java的基础语法,...

    java期末考试复习题库

    Java是一种广泛应用于软件开发的高级编程语言,它具有平台独立性、对象导向、分布式、多线程、动态链接、安全性等特点。Java语言的基本结构包括变量、数据类型、运算符、控制流语句、方法、数组、类、继承、接口、...

    JAVA课程总复习

    7. **多线程**:介绍线程的概念,创建线程的方式(继承Thread类和实现Runnable接口),线程同步和通信机制(如synchronized关键字、wait/notify、Semaphore等)。 8. **反射**:探讨如何在运行时动态获取类的信息并...

    Java Web编程技术 复习资料

    Java Web编程技术是开发基于Java平台的Web应用程序的关键技能,涵盖了诸如Servlet、JSP、JavaBean、MVC架构、框架(如Spring MVC、Struts2)以及数据库连接等多个方面。这些复习资料旨在帮助你深入理解和掌握这个...

    Java期末复习资料_第1章_Java入门_.pdf

    总结来说,Java是一种功能强大的面向对象编程语言,具备简单易用、平台无关、面向对象、多线程和动态性等特点。Java的开发流程清晰,通过遵循一定的编程风格和规范,可以编写出高质量的代码。Java的平台无关性使其...

    Java编程郭广军PPT

    3. **Java多线程技术**:Java支持多线程编程,这使得程序可以在同一时间执行多个任务。这部分可能会讲解Thread类的使用,如何创建和控制线程,线程同步机制,如synchronized关键字、wait()、notify()和notifyAll()...

    429.427.JAVA基础教程_多线程-复习:线程的创建与常用方法(429).rar

    在多线程编程中,控制线程间的交互至关重要。Java提供了多种同步机制,如synchronized关键字、wait()和notify()方法、ReentrantLock等。synchronized确保同一时间只有一个线程访问特定代码块,防止数据竞争。wait()...

    java笔试编程题(小合集)

    例如,可能会涉及到如何实现字符串反转、数组排序、链表操作,以及异常处理、多线程同步等核心概念。这些题目旨在考察应聘者的代码编写能力和问题解决能力。 第二份文档《华为Java笔试题.doc》则专门针对华为公司的...

    java学习资料JAVA编程技术复习纲要.doc

    * 多线程:Java 是第一个在语言级提供内置多线程支持的高级语言 * 动态内存分配:Java 中所有的对象都是通过动态内存分配建立的,Java 对内存自动进行管理并进行垃圾回收 知识点3:Java 程序概述 * Java 程序的...

    JAVA编程题全集(100题).doc

    6. Java 程序设计总复习题 9: 编写一个 Java 程序实现多线程,在线程中输出线程的名字,隔 300 毫秒输出一次,共输出 20 次。 经典编程程序 7. Java 程序设计总复习题 5: 编写一个 Java 应用程序,从键盘读取用户...

    Java复习资料及例题源代码

    Java支持多线程编程,复习资料会讲解线程的创建(Thread类和Runnable接口)、同步机制(synchronized关键字、wait/notify、Lock接口)、并发工具类(如Semaphore、CountDownLatch、CyclicBarrier)。例题将帮助你...

    JAVA实用编程100例

    6. **多线程编程**:Java内置对多线程的支持,包括Thread类和Runnable接口。实例将涵盖同步机制(如synchronized关键字、wait/notify、Lock接口)、并发工具类(如ExecutorService、CountDownLatch、CyclicBarrier)...

    Java语言高级编程lesson1课件和期末试题参考答案

    在Java的高级编程中,重点通常会转向多线程、异常处理、集合框架、IO流、网络编程、反射、注解以及JVM内部机制等主题。多线程允许程序同时执行多个任务,提高效率;异常处理用于捕获和处理运行时错误,保证程序的...

    (官方)Java语言程序设计(原书第8版)基础篇和进阶篇复习题和编程题答案

    8. **多线程**:Java提供了内置支持多线程的机制,通过复习题和编程题,你可以学习如何创建和管理线程,理解同步和互斥的概念,以及如何使用synchronized关键字和wait()、notify()方法。 通过详细解答这些复习题和...

    java编程考试题

    "java编程考试题"通常涵盖了一系列Java语言的基础概念、语法特性、面向对象编程、异常处理、集合框架、多线程、输入/输出(I/O)、网络编程以及Java的API使用等内容。这份“java一期内测题”很可能是为了帮助学生或...

Global site tag (gtag.js) - Google Analytics