Java 钟表(多线程)

概要:

Applet是在Web浏览器中运行的Java小应用程序,它能够嵌入到HTML页面中,并可以通过Web浏览器下载和执行。Applet程序中并不需要主运行方法,由Java虚拟机调用执行。本实例介绍运用Applet与多线程制作一个简单的钟表。

| |目录

技术要点

运用Applet与多线程制作一个简单的钟表的技术要点如下:

  • Applet程序中不需要主运行函数,取之的则是使用init()、start()、stop()和destroy()方法。这四个方法组成了Applet的生命周期。

  • Applet类位于java.Applet包中,其继承自Pod,所以也是一个容器,可以包含AWT组件。当Applet嵌入到Web页面时,如果页面转到其他页面,浏览器会自动调用Applet类的stop()方法,因此让Applet暂停的代码要写在这个方法中。

代码实现

package net.xsoftlab.baike;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Date;

public class TestClock extends Applet implements Runnable {

	private static final long serialVersionUID = -725672405974481108L;

	// 操作运用多线程实现一个钟表的类
	Thread HOUR_THREAD = null;// 表示时针的线程
	Thread MINUTE_THREAD = null;// 表示分针的线程
	Thread SECOND_THREAD = null;// 表示秒针的线程
	int hour_a, hour_b, minute_a, minute_b, second_a, second_b;// 表示时针,分针,秒针端点的整型变量
	int current_hour = 0;// 获取当前的时整数变量
	int current_minute = 0;// 获取当前的分整数变量
	int current_second = 0;// 获取当前的秒整数变量
	Graphics second_graphics = null;// 绘制秒针的Graphics对象
	Graphics minute_graphics = null;// 绘制分针的Graphics对象
	Graphics hour_graphics = null;// 绘制时针的Graphics对象
	double point_x[] = new double[61];// 存放表盘刻度的X轴数组
	double point_y[] = new double[61];// 存放表盘刻度的Y轴数组
	double scan_x[] = new double[61];// 供绘制表盘使用
	double scan_y[] = new double[61];
	int isStart = 0;// 判断是否重新开始

	@Override
	public void init() {// 数据初始化
		hour_graphics = this.getGraphics();// 实例化时针Graphics对象
		hour_graphics.setColor(Color.black);// 设置时针的颜色
		hour_graphics.translate(200, 200);// 进行坐标系统变换,原点设在(200,200)处
		minute_graphics = this.getGraphics();// 实例化分针Graphics对象
		minute_graphics.setColor(Color.blue);// 设置分针的颜色
		minute_graphics.translate(200, 200);// 进行坐标系统变换,原点设在(200,200)处
		second_graphics = this.getGraphics();// 实例化秒针Graphics对象
		second_graphics.setColor(Color.RED);// 设置秒针的颜色
		second_graphics.translate(200, 200);// 进行坐标系统变换,原点设在(200,200)处

		point_x[0] = 0;
		point_y[0] = -120;// 各个时针12点处的位置坐标(按新坐标系的坐标)
		scan_x[0] = 0;
		scan_y[0] = -140;// 12点处的刻度位置坐标(按新坐标系的坐标)
		double jiaodu = 6 * Math.PI / 180;
		for (int i = 0; i < 60; i++) {// 表盘分割成60分,将分割点的坐标存放在数组中
			point_x[i + 1] = point_x[i] * Math.cos(jiaodu) - Math.sin(jiaodu) * point_y[i];
			point_y[i + 1] = point_y[i] * Math.cos(jiaodu) + point_x[i] * Math.sin(jiaodu);
		}
		point_x[60] = 0;
		point_y[60] = -120;
		for (int i = 0; i < 60; i++) {// 表盘分割成60分,将分割点的坐标存放在绘制数组中
			scan_x[i + 1] = scan_x[i] * Math.cos(jiaodu) - Math.sin(jiaodu) * scan_y[i];
			scan_y[i + 1] = scan_y[i] * Math.cos(jiaodu) + Math.sin(jiaodu) * scan_x[i];
		}
		scan_x[60] = 0;
		scan_y[60] = -140;
	}

	@Override
	public void start() {
		if (isStart >= 1) {
			SECOND_THREAD.interrupt();// 唤醒线程
			MINUTE_THREAD.interrupt();
			HOUR_THREAD.interrupt();
		}
		HOUR_THREAD = new Thread(this);// 创建时针线程
		MINUTE_THREAD = new Thread(this);// 创建分针线程
		SECOND_THREAD = new Thread(this);// 创建秒针线程
		SECOND_THREAD.start();// 启动秒针线程
		MINUTE_THREAD.start();// 启动分针线程
		HOUR_THREAD.start();// 启动时针线程
		isStart++;
		if (isStart >= 2)
			isStart = 1;
	}

	@Override
	public void stop() {
		SECOND_THREAD.interrupt();// 唤醒线程
		MINUTE_THREAD.interrupt();// 唤醒线程
		HOUR_THREAD.interrupt();// 唤醒线程
	}

	@Override
	public void paint(Graphics g) {// 绘制图形
		this.start();
		g.drawOval(50, 50, 300, 300);// 表盘的外圈
		g.translate(200, 200);// 进行坐标系统变换
		for (int i = 0; i < 60; i++) {// 绘制表盘的小刻度和大刻度
			if (i % 5 == 0) {
				g.setColor(Color.BLACK);// 设置颜色
				g.fillOval((int) scan_x[i], (int) scan_y[i], 10, 10);
			} else
				g.fillOval((int) scan_x[i], (int) scan_y[i], 5, 5);
		}
	}

	public void run() {// 实现Thread的方法,开始线程
		Date date = new Date();// 获取本地时间
		String s = date.toString();
		current_hour = Integer.parseInt(s.substring(11, 13));// 获得当前时间的小时
		current_minute = Integer.parseInt(s.substring(14, 16));// 获取当前时间的分钟
		current_second = Integer.parseInt(s.substring(17, 19));// 获取当前时间的秒钟
		if (Thread.currentThread() == SECOND_THREAD) {// 如果当前线程是秒线程
			second_a = (int) point_x[current_second];// 秒针初始化
			second_b = (int) point_x[current_second];
			second_graphics.drawLine(0, 0, second_a, second_b);// 用背景色清除前一秒的秒针
			second_graphics.drawString("秒", second_a, second_b);
			int i = current_second;
			while (true) {
				try {
					SECOND_THREAD.sleep(1000);// 每隔一秒休眠
					Color c = getBackground();// 获取背景颜色
					second_graphics.setColor(c);// 设置秒针的颜色
					second_graphics.drawLine(0, 0, second_a, second_b);// 用背景色清除前一秒的秒针
					second_graphics.drawString("秒", second_a, second_b);
					if ((second_a == minute_a) && (second_b == minute_b)) {// 如果秒针与分针重合,恢复分针的显示
						minute_graphics.drawLine(0, 0, minute_a, minute_b);// 用背景色清除前一分的分针
						minute_graphics.drawString("分", minute_a, minute_b);
					}
					if ((second_a == hour_a) && (second_b == hour_b)) {// 如果秒针与时针重合,恢复时针的显示
						hour_graphics.drawLine(0, 0, hour_a, hour_b);// 用背景色清除前一时的时针
						hour_graphics.drawString("时", hour_a, hour_b);
					}
				} catch (InterruptedException e) {// 捕获异常
					Color c = getBackground();// 获取背景颜色
					second_graphics.setColor(c);// 设置秒针的颜色
					second_graphics.drawLine(0, 0, second_a, second_b);// 用背景色清除秒针
					second_graphics.drawString("秒", second_a, second_b);
					return;
				}
				second_a = (int) point_x[(i + 1) % 60];// 秒针向前走一个单位
				second_b = (int) point_y[(i + 1) % 60]; // 每一秒走6度(一个单位格)
				second_graphics.setColor(Color.red);// 绘制秒针的颜色
				second_graphics.drawLine(0, 0, second_a, second_b);// 用背景色清除前一秒的秒针
				second_graphics.drawString("秒", second_a, second_b);
				i++;
			}
		}
		if (Thread.currentThread() == MINUTE_THREAD) {// 如果当前线程是分线程
			minute_a = (int) point_x[current_minute];
			minute_b = (int) point_y[current_minute];
			minute_graphics.drawLine(0, 0, minute_a, minute_b);
			int i = current_minute;// 获取当前分钟
			while (true) {
				try {// 第一次过60-second秒就前进一分钟,以后每过60秒前进一分钟
					MINUTE_THREAD.sleep(1000 * 60 - current_second * 1000);
					current_second = 0;
					Color c = getBackground();// 获取背景颜色
					minute_graphics.setColor(c);// 设置分针的颜色
					minute_graphics.drawLine(0, 0, minute_a, minute_b);
					minute_graphics.drawString("分", minute_a, minute_b);

					if ((hour_a == minute_a) && (hour_b == minute_b)) {// 如果时针和分针重合
						hour_graphics.drawLine(0, 0, minute_a, minute_b);
						hour_graphics.drawString("时", hour_a, hour_b);
					}
				} catch (InterruptedException e) {
					return;
				}
				minute_a = (int) point_x[(i + 1) % 60];// 分针向前走一个单位
				minute_b = (int) point_y[(i + 1) % 60];// 每一分走6度(一个单位格)
				minute_graphics.setColor(Color.BLUE);// 绘制分针的颜色
				minute_graphics.drawLine(0, 0, minute_a, minute_b);
				minute_graphics.drawString("分", minute_a, minute_b);
				i++;
				current_second = 0;
			}
		}

		if (Thread.currentThread() == HOUR_THREAD) {// 如果当前线程是时线程
			int h = current_hour % 12;
			hour_a = (int) point_x[h * 5 + current_minute / 12];
			hour_b = (int) point_y[h * 5 + current_minute / 12];
			int i = h * 5 + current_minute / 12;
			hour_graphics.drawLine(0, 0, hour_a, hour_b);
			hour_graphics.drawString("时", hour_a, hour_b);
			while (true) {
				try {// 第一次过12-minute%12分钟就前进一个刻度,以后每过12分钟前进一个刻度
					HOUR_THREAD.sleep(1000 * 60 * 12 - 1000 * 60 * (current_minute % 12) - current_second * 1000);
					current_minute = 0;
					Color c = getBackground();
					hour_graphics.setColor(c);
					hour_graphics.drawLine(0, 0, hour_a, hour_b);
					hour_graphics.drawString("时", hour_a, hour_b);
				} catch (InterruptedException e) {
					return;
				}
				hour_a = (int) point_x[(i + 1) % 60];
				hour_b = (int) point_y[(i + 1) % 60];
				hour_graphics.setColor(Color.BLACK);
				hour_graphics.drawLine(0, 0, hour_a, hour_b);
				hour_graphics.drawString("时", hour_a, hour_b);
				i++;
				current_minute = 0;
			}
		}
	}
}

程序解读

  1. TestClock类继承Applet类实现Runnable接口,继承Applet类需要扩展init()、start()、stop()以及destroy()方法,实现接口必须实现run()方法。类中声明存放表盘刻度的横纵坐标数组和供绘制表盘用的横纵坐标数组以及其他变量。

  2. init()方法创建时针、分针以及秒针图形对象,并设置时针图形颜色为黑色,分针图形颜色为蓝色,秒针图形颜色为红色。Translate()方法进行坐标系统变换,将原点设置在(200,200处)。设置表盘刻度的横纵坐标与供绘制表盘用的横纵坐标的初始位置。运用循环将表盘分割成60等份,并将分割好的坐标存放在数组中。再将绘制表盘分割成60等份,分割好的坐标放在绘制表盘的数组中。

  3. start()方法判断标识是否大于1来唤醒秒针、分针与时针的线程。创建时针、分针与秒针线程对象并启动线程。stop()方法唤醒秒针、分针与时针线程。

  4. paint()方法用来绘制图形。drawOval()方法用来绘制表盘的外圈,translate()方法进行坐标系统的变换。运用循环绘制表盘的大刻度与小刻度以及表盘的颜色。

  5. run()方法获得本地时间并将时间转成字符串,根据字符串的substring()方法对时间字符串进行截取,截取出本地的时分秒。如果当前的线程为秒线程,则将当前的秒数放在存放秒的数组中,并用背景色清除前一秒的秒针,这样就形成了秒针的移动。秒针每隔1秒移动一次,如果秒针运动的坐标与分针重合,则恢复分针的显示;如果秒针运动的坐标与时针重合,则恢复时针的显示。秒针向前走一个单位,每一秒走的角度为6度,每走一步则用背景色清除前一秒的秒针。如果当前的线程为分线程,则将当前的分数放在存放分的数组中,并用背景色清除前一分钟的分针,这样就形成了分针的移动。分针第一次过60秒数就前进一次,以后每隔60秒移动一次,如果分针运动的坐标与时针重合,则恢复时针的显示。如果当前的线程为时线程,则将当前的时数放在存放时的数组中,并用背景色清除前一时的时针,这样就形成了时针的移动。时针第一次过(12-分数%12)便前进一个刻度,以后每隔12分钟就移动一次。


评论关闭
评论 还能输入200
评论关闭
评论 还能输入200
  • 全部评论(0)
资料加载中...
已关注 , 取消