A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© yangxiaoqiang 初级黑马   /  2018-11-26 15:33  /  775 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

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: 文件的名称          (子路径)



0 个回复

您需要登录后才可以回帖 登录 | 加入黑马