等待唤醒机制、线程池、Lambda表达式

一、等待唤醒机制

        等待唤醒机制,可以完成线程间的通信。

        相关的API(这些方法都是Object中, 而不是Thread中的)

        void wait():让线程等待。 直到有其他线程调用notify或者notifyAll唤醒这个线程.

        void wait(long timeout):让线程等待。 直到有其他线程调用notify或者notifyAll唤醒 这个线程.或者等待时间已到也会自己醒

        void notify():唤醒一个线程

        void notifyAll(): 唤醒所有线程

    上面的方法虽然都是Object中的方法,但是并不能直接通过对象去调用。

    这些方法要放在同步代码块中,并且使用锁对象去调用。

    notify方法唤醒的是当前同步代码块中等待的线程(通过哪个锁对象调用的notify,那么唤醒的就是哪个同步代码块中的线程)

    当线程调用wait方法后,会释放掉锁对象。

    wait和sleep的区别:

        wait会释放锁

        sleep不会释放锁

·

package cn.itcast.demo01;

/*

    包子铺

 

    包子铺执行的任务是生产包子

*/

public class BaoZiPu implements Runnable{

 

    //因为要保证让包子铺和吃货用的是同一个包子,那么我们可以让这个包子从外界传递过来

    //如果从外界给包子铺和吃货传递同一个包子,那么他们用的就是同一个包子了

    private BaoZi baoZi;

 

    //提供构造方法,用来让外界传递BaoZi对象,给对应的成员变量赋值

    public BaoZiPu(BaoZi baoZi) {

        this.baoZi = baoZi;

    }

 

    //run方法中是线程要执行的任务,此时线程要执行的任务是生产包子

    @Override

    public void run() {

        //包子铺要无限次的生产包子,所以可以使用死循环

        while(true) {

            //因为wait和notify要写在同步代码块中,所以定义同步代码块

            synchronized(baoZi) { //因为吃货和包子铺用的是同一个包子对象,那么可以把包子对象当做锁

                //如果此时有包子, 包子铺就不会生产, 会等着吃货去吃包子

                if(baoZi.flag) {

                    //等待,让吃货吃包子。 wait方法一定要通过锁对象去调用

                    try {

                        baoZi.wait();

                    } catch (InterruptedException e) {

                    }

                }

 

                //如果没有包子,那么就要生产包子

                System.out.println("包子铺生产了一个包子");

                //把包子的flag改成true表示包子已经存在了

                baoZi.flag = true;

                baoZi.xianer = "五仁";

                //通知吃货吃包子

                baoZi.notify(); //唤醒吃货吃包子

            }

        }

    }

}

package cn.itcast.demo01;

/*

    包子铺

 

    包子铺执行的任务是生产包子

*/

public class BaoZiPu implements Runnable{

 

    //因为要保证让包子铺和吃货用的是同一个包子,那么我们可以让这个包子从外界传递过来

    //如果从外界给包子铺和吃货传递同一个包子,那么他们用的就是同一个包子了

    private BaoZi baoZi;

 

    //提供构造方法,用来让外界传递BaoZi对象,给对应的成员变量赋值

    public BaoZiPu(BaoZi baoZi) {

        this.baoZi = baoZi;

    }

 

    //run方法中是线程要执行的任务,此时线程要执行的任务是生产包子

    @Override

    public void run() {

        //包子铺要无限次的生产包子,所以可以使用死循环

        while(true) {

            //因为wait和notify要写在同步代码块中,所以定义同步代码块

            synchronized(baoZi) { //因为吃货和包子铺用的是同一个包子对象,那么可以把包子对象当做锁

                //如果此时有包子, 包子铺就不会生产, 会等着吃货去吃包子

                if(baoZi.flag) {

                    //等待,让吃货吃包子。 wait方法一定要通过锁对象去调用

                    try {

                        baoZi.wait();

                    } catch (InterruptedException e) {

                    }

                }

 

                //如果没有包子,那么就要生产包子

                System.out.println("包子铺生产了一个包子");

                //把包子的flag改成true表示包子已经存在了

                baoZi.flag = true;

                baoZi.xianer = "五仁";

                //通知吃货吃包子

                baoZi.notify(); //唤醒吃货吃包子

            }

        }

    }

}

 

package cn.itcast.demo01;

/*

    吃货

 

    吃货要执行的任务是吃包子。

 

*/

public class ChiHuo implements Runnable{

 

    //因为吃货和包子铺用的必须是同一个包子对象,所以可以定义一个成员变量,然后从外界传递一个包子对象赋值给这个成员变量

    private BaoZi baoZi;

    public ChiHuo(BaoZi baoZi) {

        this.baoZi = baoZi;

    }

 

    //在run方法中要定义线程执行的任务,线程要执行的任务是一直吃包子。

    @Override

    public void run() {

        //因为吃货要不停的吃包子,所以可以使用死循环

        while(true) {

            //因为wait和notify要在同步代码块中调用,所以写同步代码块

            synchronized (baoZi) { //锁对象是包子,因为两个线程公用同一个包子

                //如果没有包子, 吃货应该等待

                if(!baoZi.flag) {

                    try {

                        //让该线程等待,wait方法要通过锁对象去调用

                        baoZi.wait();

                    } catch (InterruptedException e) {

                    }

                }

 

                //如果有包子,吃货应该吃包子

                System.out.println("吃货吃了一个包子," + baoZi.xianer + "的包子");

                baoZi.flag = false;

 

                //通知包子铺去生成包子

                baoZi.notify(); //唤醒包子铺线程

 

            }

        }

    }

}

 

package cn.itcast.demo01;

 

public class Demo02Thread {

    public static void main(String[] args) {

        //创建一个包子对象

        BaoZi baoZi = new BaoZi();

        //创建包子铺

        BaoZiPu baoZiPu = new BaoZiPu(baoZi);

        //创建吃货

        ChiHuo chiHuo = new ChiHuo(baoZi);

 

        //创建两个线程一个执行包子铺的任务,一个执行吃货的任务

        new Thread(baoZiPu).start();

        new Thread(chiHuo).start();

    }

}

 

public class Demo01Thread {

    public static void main(String[] args) throws InterruptedException {

        Object obj = new Object();

        obj.wait();

    }

}

    二、线程池

        

线程池:线程池就是一个容器,里面存放了很多线程,并且这些线程具有复用性,可以多次的执行任务。

 

    线程池相关API:

        Executor: 线程池的顶层接口。 可以去执行线程任务。

        ExecutorService: Executor的子接口, 可以去执行任务,以及对线程进行管理。

        Executors: 操作线程池的工具类。 这个工具类中有一些方法可以获取到线程池对象。 (线程池对象不是我们new出来的,而是通过Executors工具类获取到的)

 

    Executors 工具类中获取线程池的方法:

        static ExecutorService newFixedThreadPool(int nThreads): 获取一个线程池对象,参数nThreads为线程池中线程的数量。

 

    如果要使用线程池执行任务,可以调用ExecutorService中的submit

        submit(Runnable task): 参数要传递Runnable接口的实现类,表示线程要执行的任务

 

    线程池的使用步骤:

        1. 通过线程池工具类获取一个线程池对象。

        2. 定义Runnable接口的实现类,定义线程池要执行的任务。

        3. 调用线程池的submit方法,提交(执行)任务

        4. 销毁线程池(一般不做)

package cn.itcast.demo02;

 

public class MyRunnableImpl implements Runnable{

 

    @Override

    public void run() {

        //打印100行HelloWorld

        for(int i = 1; i <= 100; i++) {

            System.out.println(Thread.currentThread().getName() + "正在打印HelloWorld:" + i);

        }

    }

}

 

public class Demo01ThreadPool {

    public static void main(String[] args) {

        //获取一个线程池对象

        ExecutorService pool = Executors.newFixedThreadPool(2); //2表示线程池中有两个线程

        //让线程池去执行任务。

        pool.submit(new MyRunnableImpl());

        pool.submit(new MyRunnableImpl());

        pool.submit(new MyRunnableImpl());

        //销毁线程池

        pool.shutdown();

    }

}

 

三、Lambda表达式

 

  1、 Lambda 表达式是匿名内部类的简化写法。

    Lambda 标准格式:

        (参数类型 参数名) -> {

            方法体;

            return 返回值;

        }

 

    省略规则:

        1. 小括号中参数类型可以省略

        2. 小括号中只有一个参数,小括号可以省略

        3. 大括号中只有一条语句,可以省略大括号,return, 分号

 

    Lambda表达式使用前提:

        1. 必须要有接口(不能是抽象类), 并且接口中有且仅有一个需要被重写的抽象方法。 (函数式接口)

        2. 必须支持上下文推导。 必须有接口作为参数,或者使用一个接口类型的变量接收一下Lambda表达式

 

    Lambda表达式是匿名内部类的简化写法,能不能和匿名内部类无条件替换?? 不可以。

    Lambda表达式能做的,匿名内部类一定能做。 匿名内部类能做的Lambda不一定能做。

    1. 匿名内部类可以是接口,也可以是抽象类,也可以是普通父类。 但是Lambda表达式必须是接口。

    2. 匿名内部类重写的方法可以是多个。 但是Lambda表达式要求接口中必须只有一个需要被重写的抽象方法。

 

    Lambda表达式和匿名内部类原理完全不同。

    不同的地方在于他们内部使用的字节码指令完全不同。

    所以Lambda表达式并不是匿名内部类的语法糖。

    Lambda表达式使用的字节码指令:invokedynamic    动态的字节码执行。

    其他的像匿名内部类创建对象用的都是其他的静态字节码执行: invokeinterface, invokestatic ....

public class Demo08Lambda {

    public static void main(String[] args) {

 

        //使用MyInterface接收Lambda表达式,说明这个Lambda表示的是MyInterface接口中的抽象方法的内容.

        /*

        MyInterface m = (int a) -> {

            System.out.println(a);

        };

        */

 

        //把Lambda表达式当做参数

        //invokeMethod参数是一个MyInterface接口, 此时传递的Lambda表达式就可以推倒出来, 表示的是这个接口中的抽象方法的内容.

        invokeMethod((int a) -> {

            System.out.println(a);

        });

    }

 

    public static void invokeMethod(MyInterface m) {

        m.method(10);

    }

}

 

2、 Lambda省略格式

 

    Lambda标准格式写法:

        (参数类型 参数名) -> {

            方法体;

            return 返回值;

        }

 

    1. 小括号中的参数类型可以省略

    2. 如果小括号中只有一个参数,那么可以省略小括号

    3. 如果大括号中只有一条语句,那么无论这个方法有没有返回值,都可以省略大括号, return, 分号.

 

public class Demo06SimpleLambda {

    public static void main(String[] args) {

        //匿名内部类

        invokeMethod(new MyInterface() {

            @Override

            public void method(int a) {

                System.out.println(a);

            }

        });

 

        //使用Lambda表达式标准格式

        invokeMethod((int a) -> {

            System.out.println(a);

        });

 

        //小括号中的参数类型可以省略

        invokeMethod((a) -> {

            System.out.println(a);

        });

 

        //如果小括号中只有一个参数,那么可以省略小括号

        invokeMethod(a -> {

            System.out.println(a);

        });

 

        //如果大括号中只有一条语句,那么可以省略大括号,return,以及分号.

        invokeMethod(a -> System.out.println(a));

 

    }

 

    public static void invokeMethod(MyInterface m) {

        m.method(10);

    }

}

 

3、 Lambda表达式有参有返回值的练习。

 

public class Demo05LambdaTest {

 

    /*

        调用invokeCalc,传递Lambda表达式,求10和20相加之后的结果

     */

    public static void main(String[] args) {

        invokeCalc(10, 20, (int a, int b) -> {

            return a + b;

        });

    }

 

    /*

        定义方法传递Calculator接口对象,并且在方法中调用calc方法

     */

                                // a = 10, b = 20 c = Lambda表达式

    public static void invokeCalc(int a, int b, Calculator c) {

        //calc方法会对a和b进行计算

        int result = c.calc(a, b);

        System.out.println("result:" + result);

    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值