专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »Java教程 » java线程:java中线程概念描述方法 »正文

java线程:java中线程概念描述方法

来源: 发布时间:星期四, 2009年2月12日 浏览:453次 评论:0


java 中线程概念描述编写具有多线程能力经常会用到思路方法有:

  run, start, wait, noty, notyAll, sleep, yield, join

  还有个重要关键字:synchronized

  下面我们对以上内容进行讲解

  :run 和start

  举例1:

public ThreadTest extends Thread {
public void run {
for ( i = 0; i < 10; i) {
.out.pr(\" \" + i);
}
}

public void (String args) {
ThreadTest.start;
ThreadTest.start;
}
}

  这是个简单多线程run 和start 是大家都很熟悉两个思路方法把希望并行处理代码都放在run 中;stat 用于自动run
这是JAVA内在机制规定并且run 访问控制符必须是public返回值必须是void(这种说法不准确run 没有返回值)run
不带参数

  这些规定想必大家都早已知道了但你是否清楚为什么run思路方法必须声明成这样形式?这涉及到JAVA思路方法覆盖和重载规定这些内容很重要
请读者参考相关资料

   2:关键字synchronized

  有了synchronized关键字多线程运行结果将变得可以控制synchronized关键字用于保护共享数据请大家注意 \"共享数据\"
定要分清哪些数据是共享数据JAVA是面向对象设计语言所以初学者在编写多线程容易分不清哪些数据是共享数据请看下面例子:

  举例2:

public ThreadTest implements Runnable {

public synchronized void run {
for ( i = 0; i < 10; i) {
.out.pr(\" \" + i);
}
}

public void (String args) {
Runnable r1 = ThreadTest;
Runnable r2 = ThreadTest; [Page]
Thread t1 = Thread(r1);
Thread t2 = Thread(r2);
t1.start;
t2.start;
}
}

  在这个run 被加上了synchronized关键字思路方法中创建了两个线程你可能会认为此运行结果定为:0123456789
0123456789但你错了!这个中synchronized关键字保护不是共享数据(
其实在这个中synchronized关键字没有起到任何作用运行结果是不可预先确定)这个t1, t2是两个对象(r1,
r2)线程JAVA是面向对象设计语言区别对象数据是区别r1,
r2有各自run 思路方法而synchronized使同个对象多个线程
在某个时刻只有其中个线程可以访问这个对象synchronized数据每个对象都有个 \"锁标志\"
当这个对象个线程访问这个对象某个synchronized数据时这个对象所有被synchronized修饰数据将被上锁( \"锁标志\"
被当前线程拿走了)只有当前线程访问完它要访问synchronized数据时当前线程才会释放 \"锁标志\"
这样同个对象其它线程才有机会访问synchronized数据

  举例3:

public ThreadTest implements Runnable {
public synchronized void run {
for ( i = 0; i < 10; i) {
.out.pr(\" \" + i);
}
}

public void (String args) {
Runnable r = ThreadTest;
Thread t1 = Thread(r);
Thread t2 = Thread(r);
t1.start;

t2.start;
}
}

  如果你运行1000次这个输出结果也定每次都是:01234567890123456789这里synchronized保护是共享数据





t1,
t2是同个对象(r)两个线程当其中个线程(例如:t1)开始执行run 思路方法时由于run 受synchronized保护所以同个对象其他线程(
t2)无法访问synchronized思路方法(run思路方法)只有当t1执行完后t2才有机会执行

  举例4: [Page]

public ThreadTest implements Runnable {
public void run {
synchronized (this) {
for ( i = 0; i < 10; i) {
.out.pr(\" \" + i);
}
}
}

public void (String args) {
Runnable r = ThreadTest;
Thread t1 = Thread(r);
Thread t2 = Thread(r);
t1.start;
t2.start;
}
}

  这个和举例3运行结果在可能情况下应该把保护范围缩到最小可以用举例4形式this代表 \"这个对象\"没有必要把整个run 保护起来
run代码只有个for循环所以只要保护for循环就可以了

  举例5:

public ThreadTest implements Runnable {
public void run {
for ( k = 0; k < 5; k) {
.out.prln(Thread.currentThread.getName
+ \" : for loop : \" + k);

}
synchronized (this) {
for ( k = 0; k < 5; k) {
.out.prln(Thread.currentThread.getName
+ \" : synchronized for loop : \" + k);
}
}
}

public void (String args) {
Runnable r = ThreadTest;
Thread t1 = Thread(r, \"t1_name\"); [Page]
Thread t2 = Thread(r, \"t2_name\");
t1.start;
t2.start;
}
}

运行结果:t1_name : for loop : 0
t1_name : for loop : 1
t1_name : for loop : 2
t2_name : for loop : 0
t1_name : for loop : 3
t2_name : for loop : 1
t1_name : for loop : 4
t2_name : for loop : 2
t1_name : synchronized for loop : 0
t2_name : for loop : 3
t1_name : synchronized for loop : 1
t2_name : for loop : 4
t1_name : synchronized for loop : 2
t1_name : synchronized for loop : 3
t1_name : synchronized for loop : 4
t2_name : synchronized for loop : 0
t2_name : synchronized for loop : 1
t2_name : synchronized for loop : 2
t2_name : synchronized for loop : 3
t2_name : synchronized for loop : 4

  第个for循环没有受synchronized保护对于第个for循环t1,
t2可以同时访问运行结果表明t1执行到了k = 2时t2开始执行了t1首先执行完了第个for循环此时还没有执行完第个for循环(





t2刚执行到k = 2)t1开始执行第 2个for循环当t1第 2个for循环执行到k = 1时t2个for循环执行完了
t2想开始执行第 2个for循环但由于t1首先执行了第 2个for循环这个对象锁标志自然在t1手中(
synchronized思路方法执行权也就落到了t1手中)在t1没执行完第 2个for循环时候它是不会释放锁标志
所以t2必须等到t1执行完第 2个for循环后它才可以执行第 2个for循环 [Page]

3:sleep

  举例6:

public ThreadTest implements Runnable {
public void run {
for ( k = 0; k < 5; k) {
(k 2) {
try {
Thread.currentThread.sleep(5000);
}
catch (Exception e) {}
}
.out.pr(\" \" + k);
}
}

public void (String args) {
Runnable r = ThreadTest;
Thread t = Thread(r);
t.start;
}
}

  sleep思路方法会使当前线程暂停执行定时间(给其它线程运行机会)读者可以运行举例6看看结果就明白了sleep思路方法会抛出异常必须提供捕获代码

  举例7:

public ThreadTest implements Runnable {
public void run {

for ( k = 0; k < 5; k) {
(k 2) {
try {
Thread.currentThread.sleep(5000);
}
catch (Exception e) {}
}
.out.prln(Thread.currentThread.getName
+ \" : \" + k);
}
}

public void (String args) {
Runnable r = ThreadTest;
Thread t1 = Thread(r, \"t1_name\");
Thread t2 = Thread(r, \"t2_name\"); [Page]
t1.Priority(Thread.MAX_PRIORITY);
t2.Priority(Thread.MIN_PRIORITY);
t1.start;
t2.start;
}
}

  t1被设置了最高优先级t2被设置了最低优先级t1不执行完t2就没有机会执行但由于t1在执行中途休息了5秒中这使得t2就有机会执行了
读者可以运行这个试试看

  举例8:

public ThreadTest implements Runnable {
public synchronized void run {
for ( k = 0; k < 5; k) {
(k 2) {
try {
Thread.currentThread.sleep(5000);
}
catch (Exception e) {}

}
.out.prln(Thread.currentThread.getName
+ \" : \" + k);
}
}

public void (String args) {
Runnable r = ThreadTest;
Thread t1 = Thread(r, \"t1_name\");
Thread t2 = Thread(r, \"t2_name\");





t1.start;
t2.start;
}
}

  请读者首先运行举例8从运行结果上看:个线程在sleep时候并不会释放这个对象锁标志

   4:join

  举例9:

public ThreadTest implements Runnable {
public a = 0;
public void run {
for ( k = 0; k < 5; k) {
a = a + 1;
}
}

public void (String args) {
Runnable r = ThreadTest; [Page]
Thread t = Thread(r);
t.start;
.out.prln(a);
}
}

  请问输出结果是5吗?答案是:有可能其实你很难遇到输出5时候通常情况下都不是5这里不讲解为什么输出结果不是5我要讲是:
怎样才能让输出结果为5!其实很简单join 思路方法提供了这种功能join 思路方法它能够使该思路方法线程在此的前执行完毕

  把举例9 思路方法该成如下这样:

public void (String args) throws Exception {
Runnable r = ThreadTest;
Thread t = Thread(r);
t.start;
t.join;
.out.prln(a);
}

  这时输出结果肯定是5!join 思路方法会抛出异常应该提供捕获代码或留给JDK捕获

  举例10:

public ThreadTest implements Runnable {
public void run {
for ( k = 0; k < 10; k) {
.out.pr(\" \" + k);
}
}

public void (String args) throws Exception {
Runnable r = ThreadTest;
Thread t1 = Thread(r);
Thread t2 = Thread(r);
t1.start;
t1.join;
t2.start;
}
}

  运行这个看看结果是否和举例3

5:yield

  yield 思路方法和sleep 思路方法相似只是它不能由用户指定线程暂停多长时间按照SUN说法:
sleep思路方法可以使低优先级线程得到执行机会当然也可以让同优先级和高优先级线程有执行机会而yield
思路方法只能使同优先级线程有执行机会

  举例11:

public ThreadTest implements Runnable {
public void run {
8
for ( k = 0; k < 10; k) {
(k 5 && Thread.currentThread.getName.equals(\"t1\")) { [Page]
Thread.yield;
}
.out.prln(Thread.currentThread.getName
+ \" : \" + k);
}
}

public void (String args) {
Runnable r = ThreadTest;
Thread t1 = Thread(r, \"t1\");
Thread t2 = Thread(r, \"t2\");
t1.Priority(Thread.MAX_PRIORITY);
t2.Priority(Thread.MIN_PRIORITY);
t1.start;
t2.start;
}
}

输出结果:
t1 : 0
t1 : 1
t1 : 2
t1 : 3
t1 : 4
t1 : 5
t1 : 6
t1 : 7





t1 : 8
t1 : 9
t2 : 0
t2 : 1
t2 : 2
t2 : 3
t2 : 4
t2 : 5
t2 : 6
t2 : 7
t2 : 8
t2 : 9

  多次运行这个输出也是这介绍说明:yield 思路方法不会使区别优先级线程有执行机会

   6:wait, noty, notyAll

  首先介绍说明:wait, noty,
notyAll 这些思路方法由java.lang.Object类提供而上面讲到思路方法都是由java.lang.Thread类提供(
Thread类实现了Runnable接口)

  wait, noty,
notyAll 这 3个思路方法用于协调多个线程对共享数据存取所以必须在synchronized语句块内使用这 3个思路方法先看下面了例子:

  举例12:

public ThreadTest implements Runnable { [Page]
public shareVar = 0;
public synchronized void run {
(shareVar 0) {
for ( i = 0; i < 10; i) {
shareVar;
(shareVar 5) {
try {
this.wait;
}
catch (Exception e) {}
}
}
}
(shareVar != 0) {
.out.pr(Thread.currentThread.getName);
.out.prln(\" shareVar = \" + shareVar);
this.noty;
}
}

public void (String args) {
Runnable r = ThreadTest;
Thread t1 = Thread(r, \"t1\");
10
Thread t2 = Thread(r, \"t2\");
t1.start;
t2.start;
}
}

运行结果:
t2 shareVar = 5
t1 shareVar = 10

  t1线程最先执行由于状态下shareVar为0t1将使shareVar连续加1当shareVar值为5时t1wait 思路方法
t1将处于休息状态同时释放锁标志这时t2得到了锁标志开始执行shareVar值已经变为5所以t2直接输出shareVar
然后再noty 思路方法唤醒t1t1接着上次休息前进度继续执行把shareVar直加到10由于此刻shareVar值不为0
所以t1将输出此刻shareVar然后再noty 思路方法由于此刻已经没有等待锁标志线程所以此语句不起任何作用

  这个简单示范了wait, noty 使用方法读者还需要在实战中继续摸索 [Page]

   7:有关线程补充

  编写个具有多线程能力可以继承Thread类也可以实现Runnable接口在这两个思路方法中如何选择呢?从面向对象角度考虑
作者建议你实现Runnable接口有时你也必须实现Runnable接口例如当你编写具有多线程能力小应用时候

  线程调度:NewRunningRunnableOtherwise BlockedDeadBlocked in object`sit
poolBlocked in object`slock poolnoty Schedulercompletesrun start
sleep or join sleep timeout or thread join s or erupt
Lockavailablesynchronized Thread states

  terupt 个Thread对象在它生命周期中会处于各种区别状态上图形象地介绍说明了这点wa in

  start 思路方法使线程处于可运行状态这意味着它可以由JVM调度并执行这并不意味着线程就会立即运行




  实际上多个线程并不是同时执行除非线程正在真正多CPU计算机系统上执行否则线程使用单CPU必须轮流执行但是由于这发生很快
我们常常认为这些线程是同时执行

  JAVA运行时系统计划调度是抢占性如果计划调度正在运行个线程并且来了另个优先级更高线程
那么当前正在执行线程就被暂时终止而让更高优先级线程执行

  JAVA计划调度不会为和当前线程具有同样优先级个线程去抢占当前线程但是尽管计划调度本身没有时间片(
即它没有给相同优先级线程以执行用时间片)但以Thread类为基础线程系统实现可能会支持时间片分配这依赖具体操作系统
Windows和UNIX在这个问题上支持不会完全

  由于你不能肯定小应用将运行在什么操作系统上因此你不应该编写出依赖时间片分配就是说
应该使用yield思路方法以允许相同优先级线程有机会执行而不是希望每个线程都自动得到段CPU时间片

  Thread类提供给你和系统无关处理线程机制但是线程实际实现取决于JAVA运行所在操作系统因此
线程化确实是利用了支持线程操作系统

  当创建线程时可以赋予它优先级优先级越高它就越能影响运行系统
JAVA运行系统使用个负责在所有执行JAVA内运行所有存在计划调度
该计划调度实际上使用个固定优先级算法来保证每个最高优先级线程得到CPU--允许最高优先级线程在其它线程的前执行 [Page]

  对于在中有几个相同优先级线程等待执行情况该计划调度循环地选择它们当进行下次选择时选择前面没有执行线程
具有相同优先级所有线程都受到平等对待较低优先级线程在较高优先级线程已经死亡或者进入不可执行状态的后才能执行

  继续讨论wait, noty, notyAll:

  当线程执行了对个特定对象wait 那个线程被放到和那个对象相关等待池中此外wait 线程自动释放对象锁标志

  可以区别wait:wait 或wait(long timeout)

  对个特定对象执行noty 将从对象等待池中移走个任意线程并放到锁标志等待池中那里线程直在等待
直到可以获得对象锁标志notyAll 思路方法将从对象等待池中移走所有等待那个对象线程并放到锁标志等待池中
只有锁标志等待池中线程能获取对象锁标志锁标志允许线程从上次因wait 而中断地方开始继续运行

  在许多实现了wait / noty 机制系统中醒来线程必定是那个等待时间最长线程然而在Java技术中并不保证这点

  注意不管是否有线程在等待都可以noty如果对个对象noty 思路方法而在这个对象锁标志等待池中并没有线程
那么noty 将不起任何作用

  在JAVA中多线程是个神奇主题的所以说它 \"神奇\"多线程运行结果不可预测但我们又可以通过某些思路方法控制多线程执行
要想灵活使用多线程读者还需要大量实战

  另外从JDK 1.2开始SUN就不建议使用resume, stop, suspend

1

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: