线程基础
一.进程和线程
这两个概念常放在一起比较,互相理解。
简单而言,一个应用程序就是一个进程,而线程就是一个进程内部的独立的运行单位。
一个进程至少有一个线程,称为主线程。
只有一个主线程的进程称为——单线程模型。
拥有多个线程,可以并行执行的进程称为——多线程模型
二.进程和线程的区别
1)
A.多个进程之间的数据是完全独立的,要实现进程之间通信必须通过网络,不论局域网或是广域网。
B.多个线程之间是共享一块内存空间和系统资源,简单讲就是数据共享,在进程之间可以相互调用。
2)
A.进程的数据存储是要分配虚拟内存的,进程之间的切换较线程要慢
B.独立的线程的数据通常只有寄存器数据,和程序执行时使用的堆栈,即是临时的,当线程结束一次操作时,数据会自动释放,线程切换的负担很小。
3)更深层次理解线程,根据电脑不同性能,能同时实现多线程的个数也不同,但基本原理是一样的。线程运行是根据获取CPU情况而定的,获得CPU就运行,多线程只不过是CPU在它们之间快速切换,我们根本觉察不出有时间差。而且,线程类中也有很多设置CPU分配规则的方法,如休眠,优先级,。。。能灵活控制线程的运行。
单线程模式中进程则会一直占用CPU资源,直到执行完任务,效率较低,且无法实现多元化。
三.Java中的多线程实现
多线程实际就是创建多个线程对象,有一样的run方法。线程的启用时通过调用start()方法启动,从run()方法开始运行,当run()方法结束后,线程即运行结束。所以,一定要重写run方法。
Java中共有3种方法实现多线程。
分别是:1)继承Thread类 2)实现Runnable接口3)内部类实现
以下分别用这三种方法实现多线程查找文件来介绍其基本用法:
1)继承Thread类
public class FindThread extends Thread {
//构造函数传根目录的值
String rootDirName;
//构造函数
public FindThread(String rootDirName) {
this.rootDirName = rootDirName;
}
/**
* 重写run方法
*/
public void run() {
CountFile cf=new CountFile(rootDirName);//根据根目录查找文件类
int fileCount = cf.countDir(rootDirName);
System.out.println("------驱动器:" + this.rootDirName + "统计完毕:"+ ";共有文件数量:" + fileCount + " ;用时(单位:秒):" + timeCost / 1000);
}
}
//主函数中调用
for(int i=0;i<fileList.length;i++){
//创建线程对象,调用方法
FindThread ft=new FindThread(fileList[i].getAbsolutePath());
ft.start();
}
2)实现Runnable接口
public class FindRunnable implements Runnable {
String rootDirName;
//构造器
public FindRunnable(String rootDirName) {
this.rootDirName = rootDirName;
}
// 实现run方法
public void run() {
long start = System.currentTimeMillis();
CountFile cf = new CountFile(rootDirName);
int count=cf.countDir(rootDirName);
long timeCost = System.currentTimeMillis() - start;
System.out.println("------驱动器:" + this.rootDirName + "统计完毕:"+ ";共有文件数量:" + count + " ;用时(单位:秒):" + timeCost / 1000);
try {
// 每个线程暂停不同时间
Thread.sleep(10000);
//若没有休眠,线程在循环执行中会一直占用CPU资源,甚至达到100%,若一个线程要长时间循环执行某任务,就必须让线程休眠一段时间
} catch (InterruptedException ie) {
System.out.println("线程在sleep中收到中断信号,但不退出:" + ie);
}
}
}
for(int i=0;i<fileList.length;i++){
FindRunnable fr=new FindRunnable(fileList[i].getAbsolutePath());
Thread thread=new Thread(fr);//线程Thread也是实现了Runnablede 类,要启动线程,必须创建Thread对象,调用start()方法。
thread.start();
}
3)内部类实现
for(int i=0;i<fileList.length;i++){//每个磁盘都创建一个线程
final String rootDirname=fileList[i].getAbsolutePath();
Thread thread=new Thread(){//创建线程,重写run方法
public void run(){
CountFile cf=new CountFile(rootDirname);
int fileCount = cf.countDir(rootDirname);
System.out.println("------驱动器:" + rootDirname + "统计完毕:" + ";共有文件数量:" + fileCount + " ;用时(单位:秒):" + timeCost / 1000);
}
};
thread.start();
四.扩充——继承TimeTask类实现线程的定时执行
定时执行时现实生活中常有的需求,JDK提供了2个类来实现这种功能:
1)实现java.util.TimeTask类 2)调度TimeTask对象定时运行的Timer类
实现方法是:
Public void schedule(TimeTask task,long delay,long period)
它会使程序启动 delay毫秒后,没隔period毫秒执行一次。
此上其实就是闹钟的基本原理,即当设定好时间后,程序启动两个时间差后,每隔一定时间进行提醒。
//先定义一个Timer类
private static Timer timer2;
//当事件被触发,即设定闹钟时间后
timer2 = new Timer();//创建对象
int i = Integer.parseInt(tf2.getText());//得到输入的闹铃时间
//实例化一个继承TimeTask类的对象
TimerTaskDemo ttd = new TimerTaskDemo();//定时器执行的方法,弹出提醒框或音乐等
timer2.schedule(ttd, l2 - l, i * 60 * 1000);//制定计划
线程的功能十分强大,现实生活中的大多数程序都是多线程的,例如游戏,有众多人物,武器等,形象的描述多线程就是一个独立房间里每个对象都在执行自己的方法,也就是在计算机局限的环境下用多线程模拟现实生活中场景。
下一节,将以我们小组做的坦克游戏为例子,说明我对线程的学习情况。