Java语言——Lambda表达式、线程池

1. 线程间的通信

包子铺线程生产包子,顾客线程消费包子。当包子没有时(包子状态为false),顾客线程等待,包子铺线程生产包子(即包子状态为true),并通知顾客线程(解除顾客的等待状态),因为已经有包子了,那么包子铺线程进入等待状态。
接下来,顾客线程能否进一步执行则取决于锁的获取情况。如果顾客获取到锁,那么就执行吃包子动作,包子吃完(包子状态为false),并通知包子铺线程(解除包子铺的等待状态),顾客线程进入等待。包子铺线程能否进一步执行则取决于锁的获取情况。

package com.itheima.demo;

class Baozi{
    String size;
    String xian;
    boolean has=false;
}

class Guke extends Thread{
    private Baozi bz;

    public Guke(String name, Baozi bz) {
        super(name);
        this.bz = bz;
    }
    @Override
    public void run(){
        while (true){
            synchronized (bz){
                if (bz.has == false){
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("顾客正在吃"+bz.size+bz.xian+"包子");
                bz.has=false;
                bz.notify();
                System.out.println("顾客吃完了!");
            }
        }
    }
}
class Baozipu extends Thread{
    private Baozi bz;

    public Baozipu(String name, Baozi bz) {
        super(name);
        this.bz = bz;
    }
    int count=0;
    @Override
    public void run(){
        while (true){
            synchronized (bz){
                if (bz.has==true){
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("没包子了,老板正在做...");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (count%2==0){
                    bz.size="大";
                    bz.xian="猪肉";
                }
                else {
                    bz.size="小";
                    bz.xian="韭菜";
                }
                count++;
                bz.has=true;
                System.out.println("老板做好了"+bz.size+bz.xian+"包子");
                bz.notify();
            }
        }
    }
}
public class HelloWorld {
    public static void main(String[] args) {
        Baozi bz=new Baozi();
        Baozipu bzp = new Baozipu("老板",bz);
        Guke gk=new Guke("顾客",bz);
        bzp.start();
        gk.start();
    }
}

2. Lambda表达式

Lambda省去面向对象的条条框框,格式由3个部分组成:

  • 一些参数
  • 一个箭头
  • 一段代码

Lambda表达式的标准格式为:

(参数类型 参数名称) -> { 代码语句 }

格式说明:

  • 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
  • ->是Java 8新引入的语法格式,代表指向动作。
  • 大括号内的语法与传统方法体要求基本一致。

之前我们知道线程的匿名化简模式,如:

package com.itheima.demo;
public class HelloWorld {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我还要化简");
            }
        }).start();
    }
}

结果:
我还要化简

经过Lambda化简后:

package com.itheima.demo;
public class HelloWorld {
    public static void main(String[] args) {
        new Thread(() ->{
                System.out.println("我还要化简");
        }).start();
    }
}

结果:
我还要化简

此时如果run方法里只有一个语句时,还可再化简:

package com.itheima.demo;
public class HelloWorld {
    public static void main(String[] args) {
        new Thread(() -> System.out.println("我还要化简")).start();
    }
}
结果:
我还要化简

除此之外,Lambda不仅局限在线程这儿,他在其他地方也可用到。如:

1》 接口
package com.itheima.demo;

interface Cook{
    void makefood();
}
public class HelloWorld {
    public static void main(String[] args) {
        invokeCook(()-> System.out.println("吃饭了!"));
    }
    private static void invokeCook(Cook cook) {
        cook.makefood();
    }
}

或者分析:

package com.itheima.demo;

interface Calculator {
    int calc(int a, int b);
}
public class HelloWorld {
    public static void main(String[] args) {
        invokeCalc(120, 130, (int a, int b) -> {
            return a + b;
        });
    }
    private static void invokeCalc(int a, int b, Calculator calculator) {
        int result = calculator.calc(a, b);
        System.out.println("结果是:" + result);
    }
}
结果:
结果是:250
2》对象
package com.itheima.demo;

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

class Person{
    private String name;
    private int age;

    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 +
                '}';
    }
}
public class HelloWorld {
    public static void main(String[] args) {
        Person[] array = {
                new Person("古力娜扎", 19),
                new Person("迪丽热巴", 18),
                new Person("马尔扎哈", 20) };


        Arrays.sort(array, (Person o1,Person o2)->{
            return o2.getAge() - o1.getAge();
        });

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

结果:
Person{name='马尔扎哈', age=20}
Person{name='古力娜扎', age=19}
Person{name='迪丽热巴', age=18}

3. Lambda省略格式

Lambda强调的是“做什么”而不是“怎么做”,所以凡是可以根据上下文推导得知的信息,都可以省略。例如上例还可以使用Lambda的省略写法:

如之前的Calculator加法接口,还可化简为:

扫描二维码关注公众号,回复: 9087410 查看本文章
invokeCalc(120, 130, (a,b) -> a + b);

省略规则

在Lambda标准格式的基础上,使用省略写法的规则为:

  1. 小括号内参数的类型可以省略;
  2. 如果小括号内有且仅有一个参,则小括号可以省略;
  3. 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。

Lambda的使用前提

Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:

  1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法
    无论是JDK内置的RunnableComparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。
  2. 使用Lambda必须具有上下文推断
    也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

4.线程池

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

笔直向前,说到做到。

4.1 线程池的三个好处:

  1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
  2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

4.2 线程池的使用

Java里面线程池的顶级接口是java.util.concurrent.Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是java.util.concurrent.ExecutorService

Executors类中有个创建线程池的方法如下:

  • public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象。(创建的是有界线程池,也就是池中的线程个数可以指定最大数量)

获取到了一个线程池ExecutorService 对象,那么怎么使用呢,在这里定义了一个使用线程池对象的方法如下:

  • public Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行

    Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用。

使用线程池中线程对象的步骤:

  1. 创建线程池对象。
  2. 创建Runnable接口子类对象。(task)
  3. 提交Runnable接口子类对象。(take task)
  4. 关闭线程池(一般不做)。

Runnable实现类代码:

package com.itheima.demo;

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

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("我要一个教练");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("教练来了: " + Thread.currentThread().getName());
        System.out.println("教我游泳,交完后,教练回到了游泳池");
    }
}
public class HelloWorld {
    public static void main(String[] args) {
        // 创建线程池对象
        ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
        // 创建Runnable实例对象
        MyRunnable r = new MyRunnable();

        // 从线程池中获取线程对象,然后调用MyRunnable中的run()
        service.submit(r);
        // 再获取个线程对象,调用MyRunnable中的run()
        service.submit(r);
        service.submit(r);
        // 注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。
        // 将使用完的线程又归还到了线程池中
        // 关闭线程池
        service.shutdown();
    }
}

结果:
我要一个教练
我要一个教练
教练来了: pool-1-thread-2
教我游泳,交完后,教练回到了游泳池
教练来了: pool-1-thread-1
我要一个教练
教我游泳,交完后,教练回到了游泳池
教练来了: pool-1-thread-2
教我游泳,交完后,教练回到了游泳池
发布了57 篇原创文章 · 获赞 96 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44840572/article/details/103555536
今日推荐