Skip to content

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.线程状态图

1743558008072

第二章.等待唤醒机制

1.等待唤醒案例分析(线程之间的通信)

java
要求:一条线程生产,一条线程消费,不能连续生产,不能连续消费 -> 线程的通信
方法说明
wait()无线等待
notify()唤醒一条等待线程
notifyAll()唤醒所有等待线程

2.等待唤醒案例实现

1743560390344
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();
    }
}
1743561706428

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.泛型:
  <引用数据类型>-> 规定了我们操作的数据是什么类型
  <>中只能写引用数据类型,不能写基本数据类型
  泛型的作用就是为了统一类型
基本类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean
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);
    }
}
1743584040652

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);
    }
}
1743584372677

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);
    }
}