当前位置:课程学习>>第十章 多线程>>文本学习>>知识点一
一、多线程的概念
针对于多个执行的任务而言,使用线程可以把单个任务给分割开来。计算机执行某个任务一段时间后,能够执行另一个任务的一部分操作。通过这样的工作过程,可以使多个任务看起来并发执行。虽然在微观上,处理器在每一时刻仅能处理一项任务,但是通过线程执行方式,使得多项任务可以同时进行。
Java使程序开发人员能够运用并发性功能,可以指定应用程序中包含多个执行的线程,每个线程被设计成具有部分程序功能,并且能与其他线程并发执行。这种能力被称为多线程(multithreading)。
举一个简单的例子,在收看在线视频的时候,用户当然不会希望在接受完整的视频文件后才开始播放,尤其是长度较大的文件。在这种情况下,多线程技术负责将文件下载和播放功能分别设计在两个线程中,并且同时执行这两个线程。在线程执行过程中还要处理两者的执行关系,以避免播放过程中的不连续等问题。另外,较常见的还有多线程下载的例子,比如网际快车(flashget)和迅雷等工具,将文件下载过程分解成多个线程同时执行,从而提高了文件下载的效率。
为了更好的理解多线程的含义,来看一个实例。
【例10.1】编写一个程序,用以绘制小球在屏幕内弹跳的动作过程;同时提供一个按钮,来控制小球运动的开始和停止。
//Bouncer.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Bouncer extends JFrame implements ActionListener {
JPanel canvas = new JPanel();
JPanel btnPanel = new JPanel();
JButton startBtn = new JButton("start");
JButton stopBtn = new JButton("stop");
public Bouncer() {
getContentPane().add(canvas, BorderLayout.CENTER);
getContentPane().add(btnPanel, BorderLayout.SOUTH);
btnPanel.add(startBtn);
btnPanel.add(stopBtn);
startBtn.addActionListener(this);
stopBtn.addActionListener(this);
}
public static void main(String[] args) {
Bouncer bouncer = new Bouncer();
bouncer.setTitle("Bouncer");
bouncer.setSize(300, 200);
bouncer.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == startBtn) {
Ball ball = new Ball(canvas);
ball.bounce();
}
if (e.getSource() == stopBtn)
System.exit(0);
}
}
class Ball {
private JPanel canvas;
private int radio;
private int x_pos, y_pos;
private int x_dis, y_dis;
private int c_width, c_height;
public Ball(JPanel c) {
canvas = c;
c_width = canvas.getWidth();
c_height = canvas.getHeight();
radio = 6;
// 小球起始位置取随机值
x_pos = (int) (Math.random() * c_width);
y_pos = (int) (Math.random() * c_height);
// 每次移动距离为半径/2
x_dis = y_dis = radio / 2;
}
public void draw() {
Graphics g = canvas.getGraphics();
g.fillOval(x_pos, y_pos, radio, radio);
g.dispose();
}
public void move() {
Graphics g = canvas.getGraphics();
g.setXORMode(canvas.getBackground());
g.fillOval(x_pos, y_pos, radio, radio);
x_pos += x_dis;
y_pos += y_dis;
if ((x_pos <= 0) || (x_pos >= c_width - x_dis))
x_dis *= -1;
if ((y_pos <= 0) || (y_pos >= c_height - y_dis))
y_dis *= -1;
g.fillOval(x_pos, y_pos, radio, radio);
g.dispose();
}
public void bounce() {
draw();
while (1 == 1) {
move();
for (int j = 1; j <= 1000000; j++) ;
}
}
}
在实例中,绘制小球时采用XORMode方式,即在自身位置重复绘制对象时会消除原有的痕迹。程序中的小球初始位置是随机的,开始运动后将在画布内来回弹跳,draw方法中的for循环作用是调整小球的运动速度。
程序的本意是想通过按钮来控制小球的启动和停止。但是,在点击start按钮后,while循环之后的语句将不会被执行,点击stop按钮也就不可能实现,这是由于程序会一直在while循环内反复执行。为了解决这个问题,需要利用多线程的知识,在下面的内容中将逐步对多线程的内容展开介绍。
二、线程体的实现
Java提供了两种方法来创建多线程,一种是通过Thread类进行派生,另一种是实现Runnable接口。
1. 线程类Thread
Java通过java.lang.Thread类来支持多线程,在Thread类中封装了独立的有关线程执行的数据和方法。定义Thread类的派生类,这是创建线程最简单的方法。
【例10.2】利用Thread类实现多线程。
//CreateThread.java
public class CreateThread {
public static void main(String[] args) {
MyThread thread1 = new MyThread("Thread 0");
MyThread thread2 = new MyThread("Thread 1");
thread1.start();
thread2.start();
}
}
class MyThread extends Thread {
private String id;
public MyThread(String str) {
id = str;
}
public void run() {
int i = 0;
for (int j = 0; j <= 100; j++) {
System.out.println("I am " + id + "--I have run " + i + " times");
i++;
}
System.out.println(id + " is end!");
}
}
Thread类中run方法负责完成线程的具体工作,通过调用线程对象的start方法来启动线程的run方法。注意,线程工作中不能保证执行的顺序,例10.2的运行过程中线程0和线程1的数据是交替显示的,二者的结束顺序也是不可确定的。
2. Runnable接口
由于Java不支持多重继承,所以某个派生类不能再从Thread进行继承来实现线程的功能。此时要实现多线程,需要声明java.lang.Runnable接口,在Runnable接口中同样包含run方法。
【例10.3】利用Runnable接口实现多线程。
//CreateThread2.java
public class CreateThread2 {
public static void main(String[] args) {
Thread thread1 = new Thread(new MyThread2("Thread 0"));
Thread thread2 = new Thread(new MyThread2("Thread 1"));
thread1.start();
thread2.start();
}
}
class MyThread2 implements Runnable {
private String id;
public MyThread2(String str) {
id = str;
}
public void run() {
int i = 0;
for (int j = 0; j <= 100; j++) {
System.out.println("I am " + id + "--I have run " + i + " times");
i++;
}
System.out.println(id + " is end!");
}
}
MyThread2类实现了Runnable接口,将MyThread2的实例作为参数传递给Thread类的构造方法,是创建线程的另外一种方法。