介绍JAVA线程、线程类及Runnable(2)

原创 2004年08月28日 19:19:00

用Java线程获取优异性能(I)

——介绍线程、线程类及Runnable
Jeff Friesen 著 刘建华 编译



怎样使用名称
在一个调试会话期间,使用用户友好方式从另一个线程区别其中一个线程证明是有帮助的。要区分其中一个线程,Java给一个线程取一个名称。Thread缺省的名称是一个短线连字符和一个零开始的数字符号。你可以接受Java的缺省线程名称或选择使用你自己的。为了能够自定义名称,Thread提供带有name参数和一个setName(String name)方法的构造器。Thread也提供一个getName()方法返回当前名称。表2显示了怎样通过Thread(String name)创建一个自定义名称和通过在run()方法中调用getName()检索当前名称:
表2.NameThatThread.java
// NameThatThread.java
class NameThatThread
{
public static void main (String [] args)
{
MyThread mt;
if (args.length == 0)
mt = new MyThread ();
else
mt = new MyThread (args [0]);
mt.start ();
}
}
class MyThread extends Thread
{
MyThread ()
{
//编译器创建等价于super()的字节代码
}
MyThread (String name)
{
super (name); //将名称传递给Thread超类
}
public void run ()
{
System.out.println ("My name is: " + getName ());
}
}
你能够在命令行向MyThread传递一个可选的name参数。例如,java NameThatThread X 建立X作为线程的名称。如果你指定一个名称失败,你将看到下面的输出:
My name is: Thread-1
如果你喜欢,你能够在MyThread(String name)构造器中将super(name)调用改变成setName(String name)调用——作为setName(name)后一种方法调用达到同样建立线程名称的目的——作为super(name)我作为练习保留给你们。
注意
Java主要将名称指派给运行main() 方法的线程,开始线程。你特别要看看当开始线程掷出一个例外对象时在线程“main”的例外显示的JVM的缺省例外处理打印消息。
休眠或停止休眠
在这一栏后面,我将向你介绍动画——在一个表面上重复画图形,这稍微不同于完成一个运动画面。要完成动画,一个线程必须在它显示两个连续画面时中止。调用Thread的静态sleep(long millis)方法强迫一个线程中止millis毫秒。另一个线程可能中断正在休眠的线程。如果这种事发生,正在休眠的线程将醒来并从sleep(long millis)方法掷出一个InterruptedException对象。结果,调用sleep(long millis)的代码必须在一个try代码块中出现——或代码方法必须在自己的throws子句中包括InterruptedException。
为了示范sleep(long millis),我写了一个CalcPI1应用程序。这个应用程序开始了一个新线程便于用一个数学运算法则计算数学常量pi的值。当新线程计算时,开始线程通过调用sleep(long millis)中止10毫秒。在开始线程醒后,它将打印pi的值,其中新线程存贮在变量pi中。表3给出了CalcPI1的源代码:
表3. CalcPI1.java
// CalcPI1.java
class CalcPI1
{
public static void main (String [] args)
{
MyThread mt = new MyThread ();
mt.start ();
try
{
Thread.sleep (10); //休眠10毫秒
}
catch (InterruptedException e)
{
}
System.out.println ("pi = " + mt.pi);
}
}
class MyThread extends Thread
{
boolean negative = true;
double pi; //缺省初始化为0.0
public void run ()
{
for (int i = 3; i < 100000; i += 2)
{
if (negative)
pi -= (1.0 / i);
else
pi += (1.0 / i);
negative = !negative;
}
pi += 1.0;
pi *= 4.0;
System.out.println ("Finished calculating PI");
}
}
如果你运行这个程序,你将看到输出如下(但也可能不一样):
pi = -0.2146197014017295
完成计算PI
为什么输出不正确呢?毕竟,pi的值应近似等于3.14159。回答是:开始线程醒得太快了。在新线程刚开始计算pi时,开始线程就醒过来读取pi的当前值并打印其值。我们可以通过将10毫秒延迟增加为更长的值来进行补偿。这一更长的值(不幸的是它是依赖于平台的)将给新线程一个机会在开始线程醒过来之前完成计算。(后面,你将学到一种不依赖平台的技术,它将防止开始线程醒来直到新线程完成。)
注意
线程同时提供一个sleep(long millis, int nanos)方法,它将线程休眠millis 毫秒和nanos 纳秒。因为多数基于JVM的平台都不支持纳秒级的分解度,JVM 线程处理代码将纳秒数字四舍五入成毫秒数字的近似值。如果一个平台不支持毫秒级的分解度,JVM 线程处理代码将毫秒数字四舍五入成平台支持的最小级分解度的近似倍数。
它是死的还是活的?
当一个程序调用Thread的start()方法时,在一个新线程调用run()之前有一个时间段(为了初始化)。run()返回后,在JVM清除线程之前有一段时间通过。JVM认为线程立即激活优先于线程调用run(),在线程执行run()期间和run()返回后。在这时间间隔期间,Thread的isAlive()方法返回一个布尔真值。否则,方法返回一个假值。
isAlive()在一个线程需要在第一个线程能够检查其它线程的结果之前等待另一个线程完成其run()方法的情形下证明是有帮助的。实质上,那些需要等待的线程输入一个while循环。当isAlive()为其它线程返回真值时,等待线程调用sleep(long millis) (或 sleep(long millis, int nanos))周期性地休眠 (避免浪费更多的CPU循环)。一旦isAlive()返回假值,等待线程便检查其它线程的结果。
你将在哪里使用这样的技术呢?对于起动器,一个CalcPI1的修改版本怎么样,在打印pi的值前开始线程在哪里等待新线程的完成?表4的CalcPI2源代码示范了这一技术:
表4. CalcPI2.java
// CalcPI2.java
class CalcPI2
{
public static void main (String [] args)
{
MyThread mt = new MyThread ();
mt.start ();
while (mt.isAlive ())
try
{
Thread.sleep (10); //休眠10毫秒
}
catch (InterruptedException e)
{
}
System.out.println ("pi = " + mt.pi);
}
}
class MyThread extends Thread
{
boolean negative = true;
double pi; //缺省初始化成0.0
public void run ()
{
for (int i = 3; i < 100000; i += 2)
{
if (negative)
pi -= (1.0 / i);
else
pi += (1.0 / i);
negative = !negative;
}
pi += 1.0;
pi *= 4.0;
System.out.println ("Finished calculating PI");
}
}
CalcPI2的开始线程在10毫秒时间间隔休眠,直到mt.isAlive ()返回假值。当那些发生时,开始线程从它的while循环中退出并打印pi的内容。如果你运行这个程序,你将看到如下的输出(但不一定一样):
完成计算PI
pi = 3.1415726535897894
这不,现在看上去更精确了?
注意
一个线程可能对它自己调用isAlive() 方法。然而,这毫无意义,因为isAlive()将一直返回真值。
合力
因为while循环/isAlive()方法/sleep()方法技术证明是有用的,Sun将其打包进三个方法组成的一个组合里:join(),join(long millis)和join(long millis, int nanos)。当当前线程想等待其它线程结束时,经由另一个线程的线程对象引用调用join()。相反,当它想其中任意线程等待其它线程结束或等待直到millis毫秒和nanos纳秒组合通过时,当前线程调用join(long millis)或join(long millis, int nanos)。(作为sleep()方法,JVM 线程处理代码将对join(long millis)和join(long millis,int nanos)方法的参数值四舍五入。)表5的CalcPI3源代码示范了一个对join()的调用:
表5. CalcPI3.java
// CalcPI3.java
class CalcPI3
{
public static void main (String [] args)
{
MyThread mt = new MyThread ();
mt.start ();
try
{
mt.join ();
}
catch (InterruptedException e)
{
}
System.out.println ("pi = " + mt.pi);
}
}
class MyThread extends Thread
{
boolean negative = true;
double pi; //缺省初始化成0.0
public void run ()
{
for (int i = 3; i < 100000; i += 2)
{
if (negative)
pi -= (1.0 / i);
else
pi += (1.0 / i);
negative = !negative;
}
pi += 1.0;
pi *= 4.0;
System.out.println ("Finished calculating PI");
}
}
CalcPI3的开始线程等待与MyThread对象有关被mt引用的线程结束。接着开始线程打印pi的值,其值与CalcPI2的输出一样。
警告
不要试图将当前线程与其自身连接,因为这样当前线程将要永远等待。

用Runnable创建线程比较--java

  • 2008年09月25日 19:44
  • 4KB
  • 下载

黑马程序员——java第十一、十二天:多线程(创建线程1-2、多线程同步代码、实现Runnable接口、安全死锁)

------- android培训、java培训、期待与您交流! ----------  多线程概述 一个进程中至少有一个线程。 进程:是一个正在执行的程序。               ...
  • xintuyuyu
  • xintuyuyu
  • 2013年08月31日 18:37
  • 683

2、java 线程与并发程序编写--Thread与Runnable的纠结

呵呵,题目看起来有点弱。我们先讨论下游戏中的角色建模,以《Zombies VS Plants》为例: 图片(一周以后看吧)        建模时我们可以考虑设计僵尸类,他们都是活动的,OK,继承自...
  • johnyu_cn
  • johnyu_cn
  • 2011年11月21日 13:00
  • 961

线程Runnable的实现

  • 2010年12月04日 10:14
  • 1KB
  • 下载

线程类实现Runnable接口

  • 2011年11月11日 13:00
  • 1KB
  • 下载

Android线程总结笔记(2)——Handler与Runnable

这是android中实现子线程的另外一个主要的方法,Handler是子线程消息管理着,位于主线程中,用来更新UI,Runnable中的run()函数是子线程,可以在运行过程中发送消息,通知Handle...
  • lanjianhun
  • lanjianhun
  • 2013年03月19日 22:33
  • 1386

java看看我是怎么利用数组给Runnable线程传参数的1

问题来源于给Runnable 接口的线程传参数…… 多个线程,传进去的参数全不一样…… 比如进去的、出来的…… 先看个(游戏)参与者类: public class Playe2r {...
  • aw344
  • aw344
  • 2013年09月07日 18:15
  • 7101

java线程系列---Runnable和Thread的区别

 在Java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口;Thread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的r...
  • Codeperson
  • Codeperson
  • 2017年04月12日 17:27
  • 241

【Java基础】——线程Thread VS Runnable

进程和线程的关系        多个线程构成一个进程,线程是进程中的一个元素,例如QQ.exe查看电脑进程的时候发现只有一个进程,但是我们可以同时和多个用户聊天交流,而且可以一边聊天,刷空间之类。对每...
  • jiadajing267
  • jiadajing267
  • 2017年05月07日 19:39
  • 312

java 创建线程的三种方法Callable,Runnable,Thread比较及用法

编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互。一般有三种方法,Thread,Runnable,Callable.   Runnable和Callable的区别是,   (1)...
  • m0_37056211
  • m0_37056211
  • 2017年05月27日 14:08
  • 219
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:介绍JAVA线程、线程类及Runnable(2)
举报原因:
原因补充:

(最多只允许输入30个字)