day07 线程间通信 线程池 Lambda表达式线程间通信:多个线程在处理统一资源,但是多个线程的处理动作却不相同(线程的任务不同,需要协调合作)
[size=18.6667px]等待唤醒机制:
调用 wait() 和 notify() 需要注意的细节:
1. wait() 与 notify() 必须要由"同一个锁对象"调用
因为对应的锁对象可以通过 notify() 唤醒使用同一个锁对象调用的 wait() 后的线程
2. wait() 与 notify() 是属于Object类的方法
因为锁对象可以是任意对象, 而任意对象的所属类都是继承了Object类的
3. wait() 与 notify() 必须要在"同步代码块"或者是"同步方法"中使用
因为必须要通过锁对象调用这2个方法
吃包子代码实现:
1:定义一个包子类:
public class Baozi {
String pi;
String xian;
boolean you = false; // 一开始没有包子
}
2:定义一个包子铺:
public class BaoziPu extends Thread {
// 定义包子成员变量, 作为共享的数据
Baozi baozi;
// 定义有参构造方法, 便于将共享的同一个包子对象传入
public BaoziPu(Baozi baozi) {
this.baozi = baozi;
}
// 定义包子铺的任务
@Override
public void run() {
// 定义一个计数器变量, 用于判断生产什么包子
int count = 0;
// 循环生产包子
while (true) {
// 使用同步代码块
synchronized (baozi) {
// 先判断是不是有包子, 有包子则先等待
if (baozi.you) {
try {
baozi.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果能执行到这里, 说明被吃货唤醒了, 要开始做包子. 先判断做什么馅
if (count % 2 == 0) {
// 做薄皮三鲜
baozi.pi = "薄皮";
baozi.xian = "三鲜馅";
} else {
// 做冰皮牛肉
baozi.pi = "冰皮";
baozi.xian = "牛肉馅";
}
// 将计数器增加
count++;
// 打印一句话模拟包子制作过程
System.out.println("[包子铺] 正在生产" + baozi.pi + baozi.xian + "的包子");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 包子做好了, 要修改包子的状态
baozi.you = true;
// 通知吃货来吃
System.out.println("[包子铺] 做好了" + baozi.pi + baozi.xian + "的包子, 快来取餐!");
baozi.notify();
}
}
}
}
3:定义吃货类:
public class ChiHuo extends Thread {
Baozi baozi;
public ChiHuo(Baozi baozi) {
this.baozi = baozi;
}
@Override
public void run() {
// 循环吃包子
while (true) {
// 使用同步代码块
synchronized (baozi) {
// 先判断是不是没有包子, 没有包子则等待
if (!baozi.you) {
try {
baozi.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果代码能执行到这里 说明被包子铺唤醒了, 则有包子了, 开始吃包子
System.out.println("[吃货] 我正在吃" + baozi.pi + baozi.xian + "的包子, 真香!");
// 吃完了修改状态
baozi.you = false;
// 通知包子铺再去生产包子
System.out.println("[吃货] 吃完了, 老板再来一锅!");
baozi.notify();
System.out.println("---------------------------");
}
}
}
}
4测试类
public class Test {
public static void main(String[] args) {
// 创建一个包子对象, 同时传入包子铺和吃货, 作为共享的锁对象
Baozi baozi = new Baozi();
// 创建包子铺, 并开始
new BaoziPu(baozi).start();
// 创建吃货, 并开始
new ChiHuo(baozi).start();
}
}
线程池:容纳多个线程的容器,线程可以反复使用,省去了频繁创建线程对象的操作.
好处:1.降低资源消耗 2.提高响应速度 3.提高线程的可管理性
ExecutorService es = Executors.newFixedThreadPool(线程数量);
[size=18.6667px]es.submit:提交一个Runnable任务
Lambda表达式:
Lambda表达式的3个部分:
1. 一些参数 ()
接口中抽象方法的参数列表. 没参数就空着; 有参数就写, 多个参数用逗号分隔
2. 一个箭头 ->
将参数传递给方法体
3. 一段代码 {}
重写接口抽象方法的方法体
使用前提: 1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。无论是JDK内置的 Runnable 、 Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。 2. 使用Lambda必须具有上下文推断。也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
可以省略的部分:
1. (参数列表): 参数"类型"可以省略 (a, b) -> {}
2. (参数列表): 如果参数只有1个, 则"类型"和"小括号"都可以省略 a -> sout(a)
3. {一些代码}: 如果只有一条代码, 则"大括号", "return", "分号"都可以"一起省略"
day08 File类 递归笔记
File类: file:文件 directory:目录 path:路径
测试File类的获取方法:
String getAbsolutePath(): 返回此File的绝对路径名字符串
String getPath(): 获取File对象的封装路径 (创建对象时传入的路径)
String getName(): 获取File对象的文件名或目录名
long length(): 获取File表示的文件大小的字节数 (不能获取目录的大小)
File f1 = new File("d:\\a.txt"); // 测试使用绝对路径
String f1AbsolutePath = f1.getAbsolutePath();
System.out.println(f1AbsolutePath); // d:\a.txt
测试File类的判断方法
boolean exists(): 判断File对象代表的文件或目录是否实际存在
boolean isDirectory(): 判断File表示的是否为目录
boolean isFile(): 判断File表示的是否为文件
File类的删除方法
boolean createNewFile(): 当文件不存在时, 创建一个新的空文件
false: 路径已经存在(无论文件还是目录)
抛IO异常: 写的路径不符合逻辑 (Y:\\a.txt\dsfsd)
boolean delete(): 删除由此File表示的文件或目录.
删除目录时: 必须是空目录
boolean mkdir(): 创建File表示的目录 "d:\\a\\b\\c\\我的目录"
false: 1. 路径已经存在(无论文件还是目录) 2. 写的路径不符合逻辑 (Y:\\a.txt\dsfsd)
boolean mkdirs(): 创建File表示的多级目录 "d:\\a\\b\\c\\我的目录"
false: 1. 路径已经存在(无论文件还是目录) 2. 写的路径不符合逻辑 (Y:\\a.txt\ds)
File类的获取目录中内容的方法
String[] list(): 获取当前File目录下的所有子文件或目录的名字数组
File[] listFiles(): 获取当前File目录中的所有子文件或目录的File对象数组
递归:
递归思想:
遇到一个问题时, 将该问题拆解成可以解决的小问题, 如果解决不了, 继续拆解为更小的问题. 如果小问题解决了, 大问题也就能够解决
Java中实现递归的方式:
方法内部调用方法自己 (所以必须定义方法)
递归的分类:
直接递归: 方法自己调用方法
间接递归: A方法调用B方法, B方法调用C方法, C方法调用A方法
递归时的注意事项:
1. 递归要有限定条件(出口), 保证递归能够停止(就是在某种情况下方法不再调用自己), 否则会栈内存溢出
2. 递归次数不能太多, 否则会栈内存溢出
3. 构造方法不能递归
递归的使用前提:
调用方法时, 方法的主体不变, 但每次传递的参数值不同, 可以使用递归
需求:求1~n的和
分析:
求1~5的和: 5+4+3+2+1
5~1的和 = 5 + ((5-1)~1的和)
4~1的和 = 4 + ((4-1)~1的和)
3~1的和 = 3 + (2~1的和)
2~1的和 = 2 + (1~1的和)
1~1的和 = 1;
public class Test {
public static void main(String[] args) {
int sum = sum(5);
System.out.println(sum); // 15
}
// 定义方法: 求1~n的和
public static int sum(int n) {
// 递归的结束出口
if (n == 1) { // 如果问题能解决, 直接返回结果
return 1;
}
// 如果能执行到这里, 说明问题没有解决, 要递归解决
return n + sum(n-1);
}
}
递归打印多级目录
public class Test {
public static void main(String[] args) {
File dir = new File("day08");
printAllFile(dir);
}
// 将每次不能解决的问题, 定义成方法: 打印某个目录中的所有文件和目录路径
public static void printAllFile(File dir) { // 参数是目录的File对象
// 为了打印所有目录的路径, 在这里统一打印目录
System.out.println(dir);
// 先获取传入的目录中的所有子文件和子目录
File[] childFiles = dir.listFiles(); // childFiles是一个变量名, 表示所有 子File对象
for (File childFile : childFiles) {
// 判断每个 子File对象 是文件还是目录
if (childFile.isFile()) {
// 是文件, 则打印路径
System.out.println(childFile);
} else if (childFile.isDirectory()) {
// 是目录, 则递归调用方法
printAllFile(childFile); // 注意!! 参数要传递 子目录对象!!!
}
}
}
}
boolean endsWith(String suffix): 判断字符串是否以指定的字符串结尾
判断文件后缀: boolean b = "aaa.JAVA".endsWith(".java");
String toLowerCase(): 将字符串转为小写字母
String s = "aaa.JAVA".toLowerCase();
s: "aaa.java"
java.io.File类: Filter过滤器
File[] listFiles(FileFilter filter): 返回文件过滤器过滤后的File对象数组
File[] listFiles(FilenameFilter filter): 返回文件过滤器过滤后的File对象数组
java.io.FileFilter接口: 用于File对象的过滤器
boolean accept(File pathName): true则会将参数的File对象加入返回的File[], false则不加入
java.io.FilenameFilter接口: 将File对象拆分为父路径和子路径来判断的过滤器
boolean accept(File dir, String name): true则会将参数的File对象加入返回的File[], false则不加入
dir: 被找到的文件所在的目录 (父路径)
name: 文件的名称 (子路径)
|
|