多线程基础概述
1.进程与线程
进程:操作系统中正在执行的一个任务(程序),比如:QQ,微信,idea,qq音乐都称之为进程。
线程:线程是进程中的一条执行路径,比如电脑管家,在病毒查杀的同时,清理垃圾,电脑加速等操作。
2. 线程的调度(CPU)
(1)平均分配执行时间(正常)
(2)抢占式运行(设置优先级)
3. 线程的状态
(1). 新建状态(线程刚创建)
(2). 就绪状态(线程启动)
(3). 运行状态(线程得到CPU的时间片)
(4). 阻塞状态(线程还未执行完,但是CPU已经将时间片分配其他线程)
(5). 死亡状态(线程执行完毕或者正常中断)
java的多线程机制中将线程分为6种状态
将阻塞分为:限时阻塞(在一个时间段之内阻塞,过了这个时间,重新进度调度队列)以及无限阻塞(必须的到通知的情况下才能继续进度调度队列)。
4. 线程创建方式
(1).继承Thread类(重写run());
(2).实现Runnable接口(重写run()),;
(3).实现Callable接口,通过FutureTask调度(JDK5新增并发编程)(重写call());
(4).使用线程池框架Executor创建(JDK5新增并发编程)
5.三种创建方式的区别
(1). 使用Thread的方式为继承,一旦继承Thread就无法再继承其他类,扩展性存在一定影响 ;可以直接创建对象并调用start启动
(2). 实现Runnable接口,类还可以再继承其他类或者实现其他接口,扩展性方面不受影响,run方法不能抛出异常;启动线程时还是需要由Thread类启动
(3). 实现Callable接口,实现的方法call有返回值,还提供了泛型支持,并且call方法允许抛出异常,一般用于并行计算,Callable接口需要有FutureTask包装并且被Thread启动。
6.线程启动
7.守护线程
守护线程也称之为后台线程,即为其他线程提供服务的线程;守护线程会随着主线程的结束而结束;如果需要设置一条线程为守护线程,则只需要调用setDaemon(true)。
8.线程中断
推荐的线程中断方法:标记中断
(1).在线程类中声明一个全局变量作为标记(整数,布尔等);
(2).当标记为运行状态时线程正常执行;
(3).一旦将当前线程的标记状态修改为终止则不再执行线程。
利用线程实现对目录的监控
一个线程监控一个文件的修改,一个线程监控一个目录中(一级目录)是否有新文件产生,若有新文件产生或者删除文件,则显示新增文件或者删除文件的名称和修改时间。(两者间无关)
运行效果:
监控文件
import java.io.File;
import java.time.LocalDateTime;
/**
* 监控文件是否改变
* @author bxwl
* @create 2020-07-17 13:43
*/
public class FileListener extends Thread{
private File target;
/**记录文件的最后修改时间*/
private long lastModify;
public FileListener(File target){
this.target = target;
}
@Override
public void run() {
//记录文件的最后修改时间
lastModify = target.lastModified();
System.out.println("开始监控:"+ LocalDateTime.now());
while(true){
//不断获取最后修改时间
long newTime = target.lastModified();
//若最后修改时间不等,则文件被修改
if(newTime != lastModify){
System.out.println(target.getName()+"被修改,修改时间:"+Tools.getFmtTime(newTime));
//更新最后修改时间
lastModify = newTime;
}
try {
sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
监控一个一级目录
import java.io.File;
import java.time.LocalDateTime;
/**
* 监控一个目录中(一级目录)是否有新文件产生
* @author bxwl
* @create 2020-07-17 13:44
*/
public class DirListener extends Thread{
/**监控的目录*/
private File dir;
/**存储原目录的文件的数组*/
private File[] files;
public DirListener(File dir){
this.dir = dir;
}
@Override
public void run() {
//获取所有子文件对象
files = dir.listFiles();
File[] newFiles;
System.out.println("开始监控目录:"+ LocalDateTime.now());
while(true){
newFiles = dir.listFiles();
/*
判断是否有新增文件
得到的文件数组长度大于之前的数组长度,则新增了文件
*/
if(newFiles.length > files.length){
for(File f:newFiles){
//标记当前文件是新文件
boolean flag = true;
//遍历原来的数组,在其中查找是否存在新文件数组的文件
for(File f2:files){
//判断新数组中的文件是否跟原数组中的文件匹配
if(f.getName().equals(f2.getName())){
//匹配则表示不是新文件
flag = false;
}
}
//判断标记是否依然是true(是新文件)
if(flag){
System.out.println("新增的文件:"+f.getName()+"新增时间:"+Tools.getFmtTime(f.lastModified()));
}
}
//更新缓存
files = newFiles;
}else if(newFiles.length < files.length){
/*
文件数组长度小于原文件数组长度,则表示删除了文件
*/
for(File f:files){
//标记当前文件是新文件
boolean flag = true;
for(File f2:newFiles){
//判断新数组中的元素是否跟原数组中的元素匹配
if(f.getName().equals(f2.getName())){
flag = false;
}
}
//判断标记是否依然是true(是新文件)
if(flag){
System.out.println("删除的文件:"+f.getName()+"删除时间:"+Tools.getFmtTime(f.lastModified()));
}
}
//更新缓存
files = newFiles;
}
try {
//没5s监听一次
sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
处理时间的工具类
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 工具类
* @author bxwl
* @create 2020-07-17 13:59
*/
public class Tools {
/**
* 对提供的毫秒数表示的时间格式化为特定的字符串
* @param time
* @return
*/
public static String getFmtTime(long time){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date d = new Date(time);
return sdf.format(d);
}
}
主方法
import java.io.File;
/**
* @author bxwl
* @create 2020-07-17 13:46
*/
public class EnterMain {
public static void main(String[] args) {
File target = new File("D:\\新建文件夹\\java\\java学习\\test\\b.txt");
File dir = new File("D:\\新建文件夹\\java\\java学习\\test");
//创建并启动文件监控线程
FileListener fl = new FileListener(target);
fl.start();
//创建并启动目录监控线程
DirListener dl = new DirListener(dir);
dl.start();
}
}