day16. 多线程 
java
课前回顾:
  1.包装类:基本类型对应的包装类
  2.Byte Short Integer Long Float Double Character  Boolean
  3.装箱:
    valueOf()
  4.拆箱:
    intValue()
  5.实现多线程方式:
    继承Thread
    实现Runnable
    实现Callable
    线程池
  6.Thread中的方法:
    run():设置线程任务
    start():开启线程,jvm会自动执行run方法
    getName():获取线程名字
    setName(String name):设置线程名字
    sleep(long time):线程睡眠
    currentThread():获取当前正在执行的线程对象
  7.同步代码块:
    synchronized(锁对象){
    }
  8.同步方法:在方法声明上加上synchronized关键字
    非静态的默认锁:this
    静态的默认锁:当前类.class
今日重点:
  1.会使用wait和notify实现一个简单的等待唤醒案例
  2.知道sleep和wait区别
  3.手撕单例模式
  4.会使用Lambda表达式第一章.线程状态 
1.线程状态介绍 
java
  当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,有几种状态呢?在API中java.lang.Thread.State这个枚举中给出了六种线程状态:
  这里先列出各个线程状态发生的条件,下面将会对每种状态进行详细解析。| 线程状态 | 导致状态发生条件 | 
|---|---|
| NEW(新建) | 线程刚被创建,但是并未启动。还没调用 start 方法。 | 
| Runnable(可运行) | 线程可以在 java 虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。 | 
| Blocked(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入 Blocked 状态;当该线程持有锁时,该线程将变成 Runnable 状态。 | 
| Waiting(无限等待) | 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入 Waiting 状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用 notify 或者 notifyAll 方法才能够唤醒。 | 
| Timed Waiting(计时等待) | 同 waiting 状态,有几个方法有超时参数,调用他们将进入 Timed Waiting 状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有 Thread.sleep 、Object.wait。 | 
| Terminated(被终止) | 因为 run 方法正常退出而死亡,或者因为没有捕获的异常终止了 run 方法而死亡。或者调用过时方法 stop() | 
2.线程状态图 

第二章.等待唤醒机制 
1.等待唤醒案例分析(线程之间的通信) 
java
要求:一条线程生产,一条线程消费,不能连续生产,不能连续消费 -> 线程的通信| 方法 | 说明 | 
|---|---|
| wait() | 无线等待 | 
| notify() | 唤醒一条等待线程 | 
| notifyAll() | 唤醒所有等待线程 | 
2.等待唤醒案例实现 

java
public class BaoZiPu {
    private int count;
    private boolean flag;
    public BaoZiPu() {
    }
    public BaoZiPu(int count, boolean flag) {
        this.count = count;
        this.flag = flag;
    }
    /**
     * get方法专门为消费线程服务
     */
    public void getCount() {
        System.out.println("消费了第......"+count+"个包子");
    }
    /**
     * set方法专门为生产线程服务
     */
    public void setCount() {
        count++;
        System.out.println("生产了第..."+count+"个包子");
    }
    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}java
/**
 * 生产者
 */
public class Product implements Runnable {
    private BaoZiPu baoZiPu;
    public Product(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (baoZiPu) {
                //1.如果flag为true,证明有包子,生产线程等待
                if (baoZiPu.isFlag() == true) {
                    try {
                        baoZiPu.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //2.如果flag为false,证明没有包子,就要生产包子
                baoZiPu.setCount();
                //3.修改flag状态为true,证明有包子了
                baoZiPu.setFlag(true);
                //4.唤醒消费线程
                baoZiPu.notify();
            }
        }
    }
}java
/**
 * 消费者
 */
public class Consumer implements Runnable {
    private BaoZiPu baoZiPu;
    public Consumer(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (baoZiPu) {
                //1.如果flag为false,证明没有包子,消费线程等待
                if (baoZiPu.isFlag() == false) {
                    try {
                        baoZiPu.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //2.如果flag为true,证明有包子,就要消费包子
                baoZiPu.getCount();
                //3.修改flag状态为false,证明没有包子了
                baoZiPu.setFlag(false);
                //4.唤醒生产线程
                baoZiPu.notify();
            }
        }
    }
}java
public class Test01 {
    public static void main(String[] args) {
        BaoZiPu baoZiPu = new BaoZiPu();
        Product product = new Product(baoZiPu);
        Consumer consumer = new Consumer(baoZiPu);
        Thread t1 = new Thread(product);
        Thread t2 = new Thread(consumer);
        t1.start();
        t2.start();
    }
}
3.用同步方法改造等待唤醒案例 
java
public class BaoZiPu {
    private int count;
    private boolean flag;
    public BaoZiPu() {
    }
    public BaoZiPu(int count, boolean flag) {
        this.count = count;
        this.flag = flag;
    }
    /**
     * get方法专门为消费线程服务
     */
    public synchronized void getCount() {
        //1.如果flag为false,证明没有包子,消费线程等待
        if (this.flag == false) {
            try {
               this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //2.如果flag为true,证明有包子,就要消费包子
        System.out.println("消费了第......"+count+"个包子");
        //3.修改flag状态为false,证明没有包子了
        this.flag = false;
        //4.唤醒生产线程
        this.notify();
    }
    /**
     * set方法专门为生产线程服务
     */
    public synchronized void setCount() {
        //1.如果flag为true,证明有包子,生产线程等待
        if (this.flag == true) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //2.如果flag为false,证明没有包子,就要生产包子
        count++;
        System.out.println("生产了第..."+count+"个包子");
        //3.修改flag状态为true,证明有包子了
        this.flag = true;
        //4.唤醒消费线程
        this.notify();
    }
    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}java
/**
 * 生产者
 */
public class Product implements Runnable {
    private BaoZiPu baoZiPu;
    public Product(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            baoZiPu.setCount();
        }
    }
}java
/**
 * 消费者
 */
public class Consumer implements Runnable {
    private BaoZiPu baoZiPu;
    public Consumer(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
           baoZiPu.getCount();
        }
    }
}java
public class Test01 {
    public static void main(String[] args) {
        BaoZiPu baoZiPu = new BaoZiPu();
        Product product = new Product(baoZiPu);
        Consumer consumer = new Consumer(baoZiPu);
        Thread t1 = new Thread(product);
        Thread t2 = new Thread(consumer);
        t1.start();
        t2.start();
    }
}2.多等待多唤醒 
2.1.解决多生产多消费问题(if 改为 while,将 notify 改为 notifyAll) 
java
public class BaoZiPu {
    private int count;
    private boolean flag;
    public BaoZiPu() {
    }
    public BaoZiPu(int count, boolean flag) {
        this.count = count;
        this.flag = flag;
    }
    /**
     * get方法专门为消费线程服务
     */
    public synchronized void getCount() {
        //1.如果flag为false,证明没有包子,消费线程等待
        while (this.flag == false) {
            try {
               this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //2.如果flag为true,证明有包子,就要消费包子
        System.out.println("消费了第......"+count+"个包子");
        //3.修改flag状态为false,证明没有包子了
        this.flag = false;
        //4.唤醒等待线程
        this.notifyAll();
    }
    /**
     * set方法专门为生产线程服务
     */
    public synchronized void setCount() {
        //1.如果flag为true,证明有包子,生产线程等待
        while (this.flag == true) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //2.如果flag为false,证明没有包子,就要生产包子
        count++;
        System.out.println("生产了第..."+count+"个包子");
        //3.修改flag状态为true,证明有包子了
        this.flag = true;
        //4.唤醒等待线程
        this.notifyAll();
    }
    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}java
/**
 * 消费者
 */
public class Consumer implements Runnable {
    private BaoZiPu baoZiPu;
    public Consumer(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
           baoZiPu.getCount();
        }
    }
}java
/**
 * 生产者
 */
public class Product implements Runnable {
    private BaoZiPu baoZiPu;
    public Product(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            baoZiPu.setCount();
        }
    }
}java
public class Test01 {
    public static void main(String[] args) {
       BaoZiPu baoZiPu = new BaoZiPu();
       Product product = new Product(baoZiPu);
       Consumer consumer = new Consumer(baoZiPu);
       new Thread(product).start();
       new Thread(product).start();
       new Thread(product).start();
       new Thread(consumer).start();
       new Thread(consumer).start();
       new Thread(consumer).start();
    }
}第三章.设计模式 
java
设计模式(Design pattern),是一套被反复使用、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、保证代码可靠性、程序的重用性,稳定性。
1995 年,GoF(Gang of Four,四人组)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式。<大话设计模式>
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。-->创建对象
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。-->对功能进行增强
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。1.单例模式 
properties
目的:让一个类只产生一个对象,供外界使用
单:一个
例:实例,对象1.1.饿汉式: 
properties
我好饿呀,想马上要一个对象,迫不及待的想要一个对象
既然想马上来个对象,我们就需要让这个对象赶紧new出来,尽早new出来java
public class Singleton {
    /**
     * 由于我们一个类只产生一个对象
     * 所以我们就不能让外界随便new
     * 所以我们需要将构造方法私有化
     */
    private Singleton() {
    }
    /*
       由于是饿汉式,需要我们自己赶紧new一个对象出来
       所以我们需要在new的时候,将其变成静态的
       又由于我们new出来的对象不能随便让外界通过类名调用
       所以将其再变成private的
     */
    private static Singleton singleton = new Singleton();
    /**
     * 定义一个公共的接口,将内部的对象返回给外界
     */
    public static Singleton getInstance() {
        return singleton;
    }
}1.2.懒汉式: 
properties
不着急new对象,啥时候用,啥时候new,也要保证一个类只产生一个对象java
public class Singleton2 {
    /**
     * 由于我们一个类只产生一个对象
     * 所以我们就不能让外界随便new
     * 所以我们需要将构造方法私有化
     */
    private Singleton2() {
    }
    /*
       由于是懒汉式,不着急new对象了
     */
    private static Singleton2 singleton = null;
    /**
     * 定义一个公共的接口,将内部的对象返回给外界
     */
    public static Singleton2 getInstance() {
        if (singleton == null){
            synchronized (Singleton2.class){
                if (singleton == null){
                    singleton = new Singleton2();
                }
            }
        }
        return singleton;
    }
}第四章.Lambda 表达式 
1.函数式编程思想和 Lambda 表达式定义格式 
java
1.面向对象思想:讲究的是先找对象,然后调用对象中的方法 -> 过于强调找对象这个过程
  好比是:去成都 -> 过于强调怎么去
2.函数式编程思想:jdk8开始有的 -> 强调的是目的,结果,不强调过程
  比如:去成都 -> 强调的是去了还是没去java
public class Demo01Lambda {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我是线程1");
            }
        }).start();
        System.out.println("======================");
        //Lambda表达式
        new Thread(()-> System.out.println("我是线程2")).start();
    }
}2.Lambda 表达式使用前提 
java
1.必须是函数式接口作为方法参数传递
2.函数式接口:
  有且只有一个抽象方法的接口java
public interface USB {
    void open();
}java
public class Demo02Lambda {
    public static void main(String[] args) {
        method(new USB() {
            @Override
            public void open() {
                System.out.println("USB打开");
            }
        });
        System.out.println("==========================");
        method(()-> System.out.println("USB打开"));
    }
    public static void method(USB usb){
        usb.open();
    }
}3.Lambda 表达式省略规则 
java
涛哥秘籍:初学者怎么学
   1.先观察是否是函数式接口做方法参数传递
   2.如果是,考虑使用lambda表达式
   3.调用方法,实参以匿名内部类方式传递
   4.从new接口开始到重写方法的方法名结束选中,删除,别忘记再删除一个右半个大括号
   5.在剩下的重写方法的参数和方法体之间,加上 ->
省略规则:
   1.重写方法的参数类型可以省略
   2.如果重写方法的参数只有一个,所在的小括号可以省略
   3.如果重写方法的方法体中只有一句代码,所在的大括号以及分号可以省略
   4.如果重写方法的方法体中只有一句代码,而且是带return的,那么所在的大括号,分号以及return关键字都可以省略java
public interface USB {
    public abstract String open(String s);
}java
public class Test01 {
    public static void main(String[] args) {
       method(new USB() {
           @Override
           public String open(String s) {
               return s+"打开了";
           }
       });
        System.out.println("=======================");
        method((String s)->{
                return s+"打开了";
        });
        System.out.println("=======================");
        method(s -> s+"打开了");
    }
    public static void method(USB usb){
        String result = usb.open("鼠标");
        System.out.println("result = " + result);
    }
}第五章.函数式接口 
java
1.概述:有且只有一个抽象方法的接口
2.检测注解:@FunctionalInterface -> 在接口上写1.Supplier 
java
1.Supplier接口
   java.util.function.Supplier<T>接口,它意味着"供给"->我们想要什么就给什么
2.方法:
  T get() -> 我们想要什么,get方法就可以返回什么
3.需求:
   使用Supplier接口作为方法的参数
   用Lambda表达式求出int数组中的最大值
4.泛型:
  <引用数据类型>-> 规定了我们操作的数据是什么类型
  <>中只能写引用数据类型,不能写基本数据类型
  泛型的作用就是为了统一类型| 基本类型 | 包装类 | 
|---|---|
| byte | Byte | 
| short | Short | 
| int | Integer | 
| long | Long | 
| float | Float | 
| double | Double | 
| char | Character | 
| boolean | Boolean | 
java
public class Demo01Supplier {
    public static void main(String[] args) {
        method(new Supplier<Integer>() {
            @Override
            public Integer get() {
                int[] arr = {4,3,4,56,7,5,4};
                Arrays.sort(arr);
                return arr[arr.length-1];
            }
        });
        System.out.println("=========================");
        method(()->{
                int[] arr = {4,3,4,56,7,5,4};
                Arrays.sort(arr);
                return arr[arr.length-1];
        });
    }
    public static void method(Supplier<Integer> supplier){
        Integer element = supplier.get();
        System.out.println("element = " + element);
    }
}
2.Consumer 
java
java.util.function.Consumer<T>->消费型接口->操作
  方法:
    void accept(T t),意为消费一个指定泛型的数据
"消费"就是"操作",至于怎么操作,就看重写accept方法之后,方法体怎么写了java
public class Demo02Consumer {
    public static void main(String[] args) {
        method(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s.length());
            }
        },"abcdefg");
        System.out.println("=======================");
        method(s-> System.out.println(s.length()),"abcdefg");
    }
    public static void method(Consumer<String> consumer,String s){
        consumer.accept(s);
    }
}
3.Function 
java
java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据
  方法:
     R apply(T t)根据类型T参数获取类型R的结果java
public class Demo03Function {
    public static void main(String[] args) {
        method(new Function<Integer, String>() {
            @Override
            public String apply(Integer integer) {
                return integer+"";
            }
        },100);
        System.out.println("=======================");
        method(integer-> integer+"",100);
    }
    public static void method(Function<Integer,String> function,Integer i){
        String s = function.apply(i);
        System.out.println("s = " + s);
    }
}4.Predicate 
java
java.util.function.Predicate<T>接口。->判断型接口
    boolean test(T t)->用于判断的方法,返回值为boolean型java
public class Demo04Predicate {
    public static void main(String[] args) {
        method(s -> s.endsWith("g"),"abcdefg");
    }
    public static void method(Predicate<String> predicate,String s){
        boolean result = predicate.test(s);
        System.out.println("result = " + result);
    }
}