18 等待与唤醒、线程池、Lambda表达式

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

一、等待与唤醒

1. 线程状态概述

2. 等待唤醒案例

  • 状态

    • 计时等待

    • 锁阻塞

    • 无限等待:一个正在无限等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态。

  • 分析

    • 创建一个顾客线程:告知老板要的包子种类和数量,调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待)。
    • 创建一个老板线程:花了5秒做包子,做好之后,调用notify方法,唤醒顾客吃包子。

  • 实现

    • 注意:
      • 顾客与老板线程必须使用同步代码块包裹起来,保证等待与唤醒只能有一个在执行。
      • 同步使用的锁对象必须保证唯一。
      • 只有锁对象才能调用waitnotify方法。
    • Object类中的方法:
      • void wait()在其他线程调用此对象的notify()或notifyAll()方法前,导致当前线程等待。
      • void notify()唤醒此对象监视器上等待的单个线程。会执行wait()方法之后的代码。
    public class BaoZi{
        public static void main(String[] args) {
            //创建锁对象
            Object obj = new Object();
    
            //顾客线程
            new Thread(){
                @Override
                public void run() {
                    while (true) {
                        synchronized (obj) {
                            System.out.println("我想吃包子!");
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println("我吃饱了,谢谢款待!");
                        }
                    }
                }
            }.start();
    
            //老板线程
            new Thread(){
                @Override
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        synchronized (obj) {
                            System.out.println("包子做好了,快吃吧!");
                            obj.notify();
                        }
                    }
                }
            }.start();
        }
    }
    

3. Object类中wait带参方法和notifyAll方法

  • 进入TimeWaiting(计时等待)有两种方式:
    • 使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态。
    • 使用wait(long m)方法,wait方法若在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程睡醒进入到Runnable/Blocked状态。

4. 线程间通信

  • 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)不相同。

二、线程池

1. 线程池的概念与原理

  • 线程池:就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C1E29ozS-1589461245253)(https://raw.githubusercontent.com/zhugulii/picBed/master/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%8E%9F%E7%90%86.bmp)]

2. 线程池的代码实现

  • 线程池:JDK1.5之后提供的。
  • java.util.concurrent.Executors:线程池的工厂类,用来生成线程池。
  • Executors类中的静态方法:
    • public static ExecutorService newFixedThreadPool(int nThreads):创建一个可重用固定线程数的线程池。
      • 参数:int nThreads:创建线程池中包含的线程数量;
      • 返回值:ExecutorService接口:返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口编程)。
  • java.util.concurrent.ExecutorService:线程池接口
    • submit(Runnable task):用来从线程池中获取线程,调用start方法,执行线程任务。
    • 关闭/销毁线程池的方法:void shutdown()
  • 线程池的使用步骤:
    • 使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池;
    • 创建一个类,实现Runnable接口,重写run方法,设置线程任务;
    • 调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法。
    • 调用ExecutorService中的方法shutdown销毁线程池(不建议执行)。
public class SubRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "正在执行");
    }
}

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
    public static void main(String[] args) {
        ExecutorService es = Executors.newFixedThreadPool(2);
        es.submit(new SubRunnable());  // pool-1-thread-1正在执行
        es.submit(new SubRunnable());  // pool-1-thread-2正在执行
        es.submit(new SubRunnable());  // pool-1-thread-1正在执行
    }
}

三、Lambda表达式

1. 函数式编程思想概述

  • 面向对象的思想:做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情。
  • 函数式编程思想:只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程。

2. Lambda表达式的标准格式

  • 标准格式由三部分组成:
    • 一些参数
    • 一个箭头
    • 一段代码
  • 格式:(参数列表)->{一些重写方法的代码};
    • ():接口中抽象方法的参数列表;
    • ->:传递的意思,把参数传递给方法体;
    • {}:重写接口的抽象方法的方法体。

3. 使用Lambda标准格式:无参无返回

/*
给定一个厨子Cook接口,内含唯一的抽象方法makeFood,且无参数,无返回值。
使用Lambda的标准格式调用invokeCook方法,打印输出”吃饭啦!“字样。
*/

public interface Cook {
    public abstract void makeFood();
}

public class Test {
    public static void main(String[] args) {
        //使用匿名内部类
        invokedCook(new Cook() {
            @Override
            public void makeFood() {
                System.out.println("吃饭啦!");
            }
        });

        //使用lambda表达式
        invokedCook(()->{
            System.out.println("吃饭啦!");
        });
    }

    public static void invokedCook(Cook cook) {
        cook.makeFood();
    }
}

4. Lambda的参数和返回值

/*
使用数组存储多个Person对象
对数组中的Person对象使用Arrays的sort方法通过年龄进行升序排序
*/
public class Person {
   private String name;
   private int age;

   public Person() {
   }

   public Person(String name, int age) {
       this.name = name;
       this.age = age;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }

   @Override
   public String toString() {
       return "Person{" +
               "name='" + name + '\'' +
               ", age=" + age +
               '}';
   }
}

import java.util.Arrays;
import java.util.Comparator;

public class Test {
   public static void main(String[] args) {
       Person[] people = {
               new Person("朱古力", 20),
               new Person("猪猪侠",22),
               new Person("猪猪猪",18)
       };

       //使用匿名内部类
       /*Arrays.sort(people, new Comparator<Person>() {
           @Override
           public int compare(Person o1, Person o2) {
               return o1.getAge() - o2.getAge();
           }
       });*/

       //使用Lambda表达式
       Arrays.sort(people,(Person o1, Person o2) -> {
           return o1.getAge() - o2.getAge();
       });

       for (Person person : people) {
           System.out.println(person);
       }
   }
}

5. Lambda表达式:自定义接口

/*
给定一个计算器Calculator接口,内含抽象方法calc可以将两个int数字相加得到和值
使用Lambda的标准格式调用invokeCalc方法,完成120与130的相加计算
*/
public interface Calculator {
    public abstract int calc(int a, int b);
}

public class Test {
    public static void main(String[] args) {
        //使用匿名内部类
        invokeCalc(120, 130, new Calculator() {
            @Override
            public int calc(int a, int b) {
                return a + b;
            }
        });

        //使用lambda表达式
        invokeCalc(12,13,(int a, int b)->{
            return a + b;
        });
    }
    public static void invokeCalc(int a, int b, Calculator c){
        System.out.println(c.calc(a, b));
    }
}

6. Lambda省略格式与使用前提

  • 省略规则:
    • 小括号内参数的类型可以省略;
    • 如果小括号内有且仅有一个参,则小括号可以省略;
    • 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。
      • 注意:大括号、return关键字及语句分号必须同时一起省略。
  • 使用前提:
    • 使用 Lambda 必须具有接口,且要求接口中有且仅有一个抽象方法
      无论是 JDK 内置的RunnableComparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用 Lambda。
    • 使用 Lambda 必须具有上下文推断
      也就是方法的参数或局部变量类型必须为 Lambda 对应的接口类型,才能使用 Lambda 作为该接口的实例。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值