介绍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。(转)

摘要 用户期望程序能展现优异的性能。为了满足这个期望,你的程序常常使用到线程。在这篇文章中我们开始练习使用线程。你将学习到线程、线程类及Runnable。 用户不喜欢反应迟钝的软件。当用户单击一个鼠标...
  • nighthawk
  • nighthawk
  • 2004-07-30 09:53:00
  • 1441

介绍Java中线程、线程类及Runnable

用Java线程获取优异性能(I)——介绍线程、线程类及Runnable Jeff Friesen 著 刘建华 编译 摘要用户期望程序能展现优异的性能。为了满足这个期望,你的程序常常使用到线程。在这篇文...
  • duoshanx
  • duoshanx
  • 2004-08-28 19:17:00
  • 7030

JAVA之旅(十二)——Thread,run和start的特点,线程运行状态,获取线程对象和名称,多线程实例演示,使用Runnable接口

JAVA之旅(十二)——Thread,run和start的特点,线程运行状态,获取线程对象和名称,多线程实例演示,使用Runnable接口 开始挑战一些难度了,线程和I/O方面的操作了,继续坚持 ...
  • qq_26787115
  • qq_26787115
  • 2016-06-03 22:43:37
  • 4976

Java基础-23总结多线程,线程实现Runnable接口,线程名字获取和设置,线程控制,线程安全,同步线程

你需要的是什么,直接评论留言。 获取更多资源加微信公众号“Java帮帮” (是公众号,不是微信好友哦) 还有“Java帮帮”今日头条号,技术文章与新闻,每日更新,欢迎阅读 ...
  • s1547823103
  • s1547823103
  • 2017-02-10 17:49:46
  • 1782

线程的实现(通过实现Runnable接口启动线程)

线程的实现(通过实现Runnable接口启动线程)
  • wangyanming123
  • wangyanming123
  • 2016-04-06 16:46:18
  • 5547

Java多线程-(4)线程的创建、启动、Runnable和Thread

一、定义线程 在Java中,多线程的实现有两种方式: 1、扩展java.lang.Thread类 2、实现java.lang.Runnable接口 二、实例化线...
  • u011936381
  • u011936381
  • 2013-12-21 17:01:05
  • 2480

java中如何给Runnable线程传递参数?

向线程中传递数据的三种方法: 一、通过构造函数传递参数 public class MyThread1 extends Thread {     private String n...
  • marvel_cheng
  • marvel_cheng
  • 2016-07-22 10:01:56
  • 15812

Java 中多线程的实现方法之Thread和Runnable

【该博文摘抄于百度知道,原文链接为:http://zhidao.baidu.com/question/164816755.html?fr=ala&word=java%E4%B8%AD%E7%9A%84...
  • u012719153
  • u012719153
  • 2015-04-02 11:16:04
  • 1214

JAVA 几种多线程的简单实例 Thread Runnable

实例1:class Hello extends Thread{private String name;public Hello(){}public Hello(String name){this.na...
  • w_yunlong
  • w_yunlong
  • 2015-12-29 11:58:22
  • 3490

Java线程学习笔记(一)---启动和停止线程

线程是进程中的一个实体,是被系统独立调度和分派的基本单位。线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属于一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一...
  • sdtvyyb_007
  • sdtvyyb_007
  • 2015-12-02 16:55:34
  • 2727
收藏助手
不良信息举报
您举报文章:介绍JAVA线程、线程类及Runnable(2)
举报原因:
原因补充:

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