多线程入门
1,创建自定义线程类的两种方式
- 继承Thread类,重写run()方法
- 用一个类实现Runnable接口,重写run()方法,再将该类的对象实例作为new Thread()的构造参数传入。
上述方法都本质都是:重写run()方法, new Thread().start 开启一个新线程。
实现Runnable接口比继承Thread类所具有的优势:
- 适合多个相同的程序代码的线程去共享同一个资源。
例如:模拟多个卖票窗口的余票时,只需创建一个Runnable实现类的实例,该实例便可以构造多个线程,实例中的参数(资源)就可以为多个线程共享,显然比继承Thread类要更加方便。 - 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
- 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
2,实现线程安全的两种方式
synchronized()
{ 需要同步操作的代码 }
Lock lock = new ReentrantLock();
lock.lock
{需要上锁的代码}
lock.unlock
3,线程的六种状态
线程状态 | 导致状态发生条件 |
---|---|
NEW(新建) | 线程刚被创建,但是并未启动。还没调用start方法。 |
Runnable(可运行) | 线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。 |
Blocked(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状 态;当该线程持有锁时,该线程将变成Runnable状态。 |
Waiting(无限等待) | 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。 |
Timed Waiting(计时等待) | 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep , Object.wait。 |
Teminated(被终止) | 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。 |
4,生产者消费者问题
问题模型:生产者和消费者共享同一资源,生产者负责制造该资源,消费者负责消耗该资源。资源有两种状态,有或无。
有资源:生产者进入waiting状态(资源调用wait()方法),消费者消费,直到该资源状态变成:无资源,资源调用notify()方法通知外面的线程。
无资源:消费者进入waiting状态(资源调用wait()方法),生产者生产,直到该资源状态变成:有资源,资源调用notify()方法通知外面的线程。
在写代码时有一些要注意的点:
- 多个不同的线程类如何共享一个资源:自定义该线程的构造方法,把要共享的资源作为构造参数传入,那么在主程序里创造的资源对象的实例就可以被两个乃至多个线程共享
synchronized(获取锁的地方){
工作内容
}
什么意思呢,synchronized参数是获取锁的地方,可以是任何引用类型,可以是一只猫,一只狗,一个字符串等引用类型,只是之后你要获取的wait(), notify()等方法都要从这个“地方”来调用,否则会发生 IllegalMonitorStateException
5,线程池的创建(入门了解)
ExecutorService service = Executors.newFixedThreadPool(5); //创建一个有五个线程的线程池
MyRunnable r = new MyRunnable(); //Runnable工具人,作为参数创建新线程
service.submit(r); //submit一次Runnable对象实例,创建一个新线程
service.submit(r);
service.submit(r);
6,Lambda表达式
本质:简化只有一个抽象方法的接口的匿名内部实现类
举个例子解释一下:调用Arrays.sort()方法时可以自定义排序方式,只要new一个
Comparator对象并且实现其接口内的compare方法,采用匿名内部类的写法:
Arrays.sort(array, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
Lambda表达式:
- 不用new新的对象
-
(要操作的参数)-> {操作过程}
根据上面方法,我们尝试把上面的写法改成Lambda表达式
Arrays.sort(array, (o1,o2) -> {o1.getAge - o2.getAge})
所以函数式接口也可以理解成,只有一个抽象方法的接口
流 入门
1,File类
new 一个File对象本质是一个包含路径信息的对象!只有路径
- 判断功能的方法:
public boolean exists()
:此File表示的文件或目录是否实际存在。
public boolean isDirectory()
:此File表示的是否为目录。
public boolean isFile()
:此File表示的是否为文件。 - 创建删除功能的方法
public boolean createNewFile()
:当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
public boolean delete()
:删除由此File表示的文件或目录。
public boolean mkdir()
:创建由此File表示的目录。
public boolean mkdirs()
:创建由此File表示的目录,包括任何必需但不存在的父目录。
- 目录的遍历
public String[] list()
:返回一个String数组,表示该File目录中的所有子文件或目录。
public File[] listFiles()
:返回一个File数组,表示该File目录中的所有的子文件或目录。
递归遍历目录与文件搜索
/*
(1.获取子文件目录
2.遍历子文件目录
如果:是文件:如果:是对应文件:打印输出
否则:是目录,)重复上面括号中的内容
*/
public static void printDir(File dir) {
// 获取子文件和目录
File[] files = dir.listFiles();
// 循环打印
for (File file : files) {
if (file.isFile()) {
// 是文件,判断文件名并输出文件绝对路径
if (file.getName().endsWith(".java")) {
System.out.println("文件名:" + file.getAbsolutePath());
}
} else {
// 是目录,继续遍历,形成递归
printDir(file);
}
}
}
}