8.1 什么是线程

def: 线程是程序内的一个单一的顺序控制流程

作为一个顺序的控制流程,线程必须在运行它的程序中占用一些资源。例如,线程必须有它自己的执行堆栈和程序计数器。在线程内运行的代码只在此上下文中工作。其他一些文章将线程称为执行上下文(execution context)。

如果你的程序必须反复执行一个任务,那么应该考虑使用java.util.Timer类。对于在一段程序后执行另一个任务,Timer类也很有用。

如果你正在编写一个具有图形的用户界面(GUI)的程序,那么应该使用javax.swing.Timer类。

对线程的基本支持由java.lang.Thread类提供。它提供了一个线程API并提供了线程的所有共有行为。这些行为包括启动、睡眠、运行、放弃和给予优先级。要使用Thread类实现线程,需要为它提供一个run方法,run方法实际执行线程的任务。

8.2 使用Timer和TimerTask类

Reminder.java这个例子使用计时器在一段延迟时间之后执行一个任务。

import java.util.Timer;

import java.util.TimerTask;

public class Reminder{

Timer timer;

public Reminder(int seconds){

timer = new Timer();

timer.schedule(new RemindTask(), seconds*1000);

}

class RemindTask extends TimerTask{

public void run(){

System.out.println("Time's up!");

timer.cancel(); //Terminate the timer thread

}

}

public static void main(String args[]){

new Reminder(5);

System.out.println("Task scheduled.");

}

}

实现和调度有计时器线程执行的任务的基本部分。

1、  实现一个定制的TimerTask子类。run方法包含执行任务的代码。

2、  通过实例化Timer类创建一个线程。

3、  实例化一个计时器任务对象(new RenmindTask())。

4、  调度这个计时器任务的执行。本例使用schedule方法。

让一个任务在特定时间执行:

import java.util.Timer;

import java.util.TimerTask;

import java.util.Calendar;

import java.util.Date;

public class Reminder{

Timer timer;

public Reminder(int seconds){

timer = new Timer();

timer.schedule(new ReminderTask(), seconds*1000);

}

public Reminder(){

Calendar calendar = Calendar.getInstance();

calendar.set(Calendar.HOUR_OF_DAY, 11);

calendar.set(Calendar.MINUTE, 9);

calendar.set(Calendar.SECOND, 0);

Date time = calendar.getTime();

timer = new Timer();

timer.schedule(new RemindTask2(), time);

}

class ReminderTask extends TimerTask{

public void run(){

System.out.println("Time's up!");

timer.cancel(); //Terminate the timer thread

}

}

class RemindTask2 extends TimerTask{

public void run(){

System.out.println("Time's up! Ring on.");

timer.cancel(); //Terminate the timer thread

}

}

public static void main(String args[]){

new Reminder(5);

new Reminder();

System.out.println("Task scheduled.");

}

}

在默认情况下,只要程序的计时器线程在运行,程序就一直运行。有四种方式可以终止计时器线程。

1、  在计时器线程上调用cancel方法。

2、  使计时器成为“守护线程”(deamon),办法是这样创建计时器: new Timer(true) 。如果程序中仅剩下守护线程,那么程序退出。

3、  在计时器的被调度的所有任务都完成后,删除所有对Timer对象的引用。最后计时器的线程将退出。

4、  调用System.exit方法,使整个程序退出。

重复执行任务:

import java.util.Timer;

import java.util.TimerTask;

import java.awt.Toolkit;

/**

* Schedule a task that executes once every second.

*/

public class AnnoyingBeep {

Toolkit toolkit;

Timer timer;

public AnnoyingBeep() {

toolkit = Toolkit.getDefaultToolkit();

timer = new Timer();

timer.schedule(new RemindTask(),

0,        //initial delay

1*1000);  //subsequent rate

}

class RemindTask extends TimerTask {

int numWarningBeeps = 3;

public void run(){

if (numWarningBeeps > 0) {

toolkit.beep();

System.out.println("Beep!");

numWarningBeeps--;

} else {

toolkit.beep();

System.out.println("Time's up!");

//timer.cancel(); //Not necessary because we call System.exit

System.exit(0);   //Stops the AWT thread (and everything else)

}

}

}

public static void main(String args[]) {

System.out.println("About to schedule task.");

new AnnoyingBeep();

System.out.println("Task scheduled.");

}

}

AnnoyingBeep程序使用schedule方法的三参数版本,指定它的任务应该马上开始执行,并且每1秒执行一次。下面是所有可用来调度任务反复执行的方法:

schedule(TimerTask task, long delay, long period)

schedule(TimerTask task, Date time, long period)

scheduleAtFixedRate(TimerTask task, long delay, long period)

scheduleAtFixedRate(TimerTask task, Date firstTime, long period)

在调度任务反复执行时,如果执行的平滑性很重要,那么应该使用schedule方法之一,如果一次蜂鸣声因为某种原因推迟了,那么后续的所有的蜂鸣声将相应延迟;如果时间的同步更重要,那么应该使用scheduleAtFixedRate方法之一,如果想让程序在第一次蜂鸣后正好3秒退出,,如果一次蜂鸣声因为某种原因延迟了,那么两次蜂鸣的时间可能较近,小于1秒。

8.3 定制线程的run方法

有两种技术可以为线程提供run方法:

1、  对Thread类进行子类化并覆盖run方法

2、  实现Runnable接口

对Thread类进行子类化并覆盖run方法

Thread类本身是一个Runnable对象。

public class SimpleThread extends Thread{

public SimpleThread(String str){

super(str);

}

public void run(){

for(int i = 0; i < 10; i++){

System.out.println(i + " " + getName());

try{

sleep((int)(Math.random()*1000));

}catch(InterruptedException e){}

}

System.out.println("Done! " + getName());

}

}

第一个方法是构造器,调用超类构造器设置线程名称。

public class TwoThreadDemo{

public static void main(String[] args){

new SimpleThread("Jamaica").start();

new SimpleThread("Fiji").start();

}

}

实现Runnable接口

import java.awt.Graphics;

import java.util.*;

import java.text.DateFormat;

import java.applet.Applet;

public class Clock extends Applet implements Runnable {

private Thread clockThread = null;

public void start(){

if(clockThread == null){

clockThread = new Thread(this, "Clock");

clockThread.start();

}

}

public void run(){

Thread myThread = Thread.currentThread();

while(clockThread == myThread){

repaint();

try{

Thread.sleep(1000);

}catch(InterruptedException e){

//the VM doesn't want us to sleep anymore;

//so get back to work

}

}

}

public void paint(Graphics g){

//get the time and convert it to a date

Calendar cal = Calendar.getInstance();

Date date = cal.getTime();

//format it and display it

DateFormat dateFormatter = DateFormat.getTimeInstance();

g.drawString(dateFormatter.format(date), 5, 10);

}

//overrides Applet's stop method, not Thread's

public void stop(){

clockThread = null;

}

}

<html>

<head>

<title>Clock</title>

</head>

<body>

I'm now listening to OuRuoLa

<APPLET CODE= "Clock.class" WIDTH=150 HEIGHT=35></APPLET>

</body>

</html>

这个Clock applet 显示当前时间并且每1秒更新一次。问题是会间接的闪烁,或许和CPU的工作状态有关。用appletviewer Clock.html查看。

Clock applet的run方法进行循环,知道浏览器要求他停止。在循环的每次迭代期间,时钟重新绘制它的显示。

如果你的泪必须子类化另一个类,那么应该使用Runnable接口。

8.4 线程的生存周期

1、创建线程

clockThread = new Thread(this, "Clock"); 执行后Clock applet处于”新线程”状态。此时线程仅仅是一个空的Thread对象,这时只能启动线程,调用除start方法外的任何方法都是无意义的,而且会导致IllegalThreadStateException。实际上,只要在线程上调用一个方法,而此线程的状态不允许此方法调用,那么运行时系统都会抛出IllegalThreadStateException。

2、启动线程

clockThread.start(); start方法分配线程所需的系统资源,调度线程运行,并调用线程的run方法。

start方法返回后,线程处于“正在运行”状态。实际情况要更复杂。如果计算机只有一个处理器,这样就不可能同时运行所有”正在运行“的线程。Java运行时环境必须实现一个调度方案,以便在所有”正在运行“的线程分享处理器。所以在任何给定时刻,”正在运行”的线程可能正在等待轮到它使用CPU。

public void run(){

Thread myThread = Thread.currentThread();

while(clockThread == myThread){

repaint();

try{

Thread.sleep(1000);

}catch(InterruptedException e){

//the VM doesn't want us to sleep anymore;

//so get back to work

}

}

}

Clock的run方法在条件clockThread == myThread成立时一直循环。它使线程和applet平缓地退出。在循环中,applet重新绘制本身,然后让线程睡眠1000毫秒。

1、  使线程不可运行

当以下事件之一发生时,线程变成“不可运行”状态:

它的sleep方法被调用;

线程调用wait方法等待某个条件得到满足;

线程因I/O而阻塞。

对于每个进入“不可运行“状态的入口,有一个特定的不同出口将线程返回到”可运行“状态。

如果一个线程已经进入睡眠状态,那么必须经过指定的睡眠时间(毫秒数);

如果一个线程正在等待某个条件,那么另一个对象必须通过调用notify或notifyAll告知正在等待的线程条件发生了改变;

如果一个线程因I/O而阻塞,那么I/O必须完成。

2、  停止线程

自然消亡;

while(clockThread == myThread)这个条件表示,当前执行的线程不等于clockThread时循环退出。当你离开页面时,运行此applet的应用程序调用applet的stop方法。然后,这个方法设置clockThread为null,由此让主循环终止run方法。如果重新访问这个页面,那么再次调用start方法,时钟在一个新线程中再次启动。即使停止和启动applet的时间快于循环一次的迭代,clockThread线程也将不同于myThread,循环将终止。

3、  isAlive方法

返回false,线程处于”新线程“状态,或者已经消亡。返回true,线程处于”可运行“或者”不可运行“。

Java语言导学笔记 Chapter 8 Thread的更多相关文章

  1. Java语言导学笔记 Chapter 9 IO

    java.io 9.1.1 字符流 Reader为读取器(reader)提供API和部分实现,读取器是读取16位字符的流: Writer为写出器(writer)提供API和部分实现,写出器是写16位字 ...

  2. java语言导学(5版)--第12章并发之二

    1不可变对象 概念:(immutable)对象创建后,状态不可更改.不可变对象在并发程序中尤其有用,因状态不可变,不会被线程干扰,也不会出现不一致状态. 书中通过实例是可变的类,并从此类衍生出一个不可 ...

  3. Java编程思想总结笔记Chapter 3

    本章需要总结的不多,但细节的东西需要注意,有些很容易遗忘. 第三章 目录: 3.1 更简单的打印语句 3.2 使用Java操作符 3.3 优先级 3.4 赋值 3.5 算数操作符 3.6 自动递增和递 ...

  4. 《C++语言导学》小记

    我看的这本是Bjarne Stroustrup写的,南开大学的杨巨峰和王刚译的.这本书不适合初学者看,我就是大概翻了翻其中感兴趣的章节. 这本书第14章的标题是“历史和兼容性”,这节内容我看了收获很深 ...

  5. 076 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 01 Java面向对象导学

    076 01 Android 零基础入门 02 Java面向对象 01 Java面向对象基础 01 初识面向对象 01 Java面向对象导学 本文知识点:Java面向对象导学 说明:因为时间紧张,本人 ...

  6. Java编程思想总结笔记Chapter 2

    本章介绍Java程序的基本组成部分,体会到Java中几乎一切都是对象. 第二章   一切都是对象 目录: 2.1 用引用操纵对象 2.2 必须由你创建所有对象 2.3 永远不需要销毁对象 2.4 创建 ...

  7. Java编程思想总结笔记Chapter 5

    初始化和清理是涉及安全的两个问题.本章简单的介绍“垃圾回收器”及初始化知识. 第五章  初始化与清理 目录:5.1 用构造器确保初始化5.2 方法重载5.3 默认构造器5.4 this关键字5.5 清 ...

  8. Java编程思想总结笔记The first chapter

    总觉得书中太啰嗦,看完总结后方便日后回忆,本想偷懒网上找别人的总结,无奈找不到好的,只好自食其力,尽量总结得最好. 第一章  对象导论 看到对象导论觉得这本书 目录: 1.1 抽象过程1.2 每个对象 ...

  9. 菜鸟笔记 -- Chapter 4 Java语言基础

    在Chapter3中我们写了第一个Java程序Hello World,并且对此程序进行了分析和常见错误解析.那么我们有没有认真观察一下Java程序的基本结构呢?本节我就来聊一下Java程序的基本结构( ...

随机推荐

  1. Codeforces Round #312 (Div. 2) ABC题解

    [比赛链接]click here~~ A. Lala Land and Apple Trees: [题意]: AMR住在拉拉土地. 拉拉土地是一个很漂亮的国家,位于坐标线.拉拉土地是与著名的苹果树越来 ...

  2. hdu 4775 Infinite Go(暴力)

    pid=4775" target="_blank" style="">题目链接:hdu 4775 Infinite Go 题目大意:两个人下围棋 ...

  3. EK中fromCharCode和parseInt的配合使用

    基于web的漏洞攻击的第一步一般是:在landing page中通过<script>标签下的JavaScript脚本引入一些恶意链接.这些脚本往往会採用各种各样的混淆.加密手法来躲避AV和 ...

  4. animate CSS动画程序接口(仅Chrome可用)

    jQuery中很早就提供了animate方法,使用它可以很方便地实现一些简单动画效果.后来CSS3中也提供了animation用于动画效果制作,但CSS本身的可操作性太差,所以用起来并不方便.现在最新 ...

  5. Sublime Text 3 无法使用package control安装插件解决办法

    Crossing's Blog NOT Genius but Try Best 首页 分类 关于 归档 标签 问题貌似出现在liveStyle版本更新之后,因为打算安装javascript next和 ...

  6. iOS开发之git学习

    本人是参考廖雪峰的git学习的.他写的非常详细,我在这里就是把我学习中的总结以及碰到的坑写出来. /* 初始化git仓库:git init */ /* 添加文件到git仓库 */ 分两步: 第一步:追 ...

  7. C#.net 摄像头驱动程序,用avicap32.dll

    装了摄像头后一般会有 avicap32.dll文件,没有一样可以用这些代码. 不需要在解决方案里面引用这个.dll文件. 下面有二种写法的例子: 例一: using System;using Syst ...

  8. HDU2094(产生冠军)题解

    HDU2094(产生冠军)题解 以防万一,题目原文和链接均附在文末.那么先是题目分析: [一句话题意] 根据给定现有比赛结果推断分析冠军.(这描述...我建议还是看题吧,题不长) [题目分析] 给出的 ...

  9. VirtualBox安装linux增强工具报错

    错误提示: Building the OpenGL support module                         [FAILED] 解决办法 cd /media/VBOXADDITIO ...

  10. Linux man 后面的数字含义及作用

    Linux的man很强大,该手册分成很多section,使用man时可以指定不同的section来浏览,各个section意义如下: 1 Executable programs or shell co ...