Java教程

我的java学习第七天

本文主要是介绍我的java学习第七天,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Day7

1.等待唤醒机制

定义

这是多个线程间的一种协作机制。谈到线程我们经常想到的是线程间的竞争(race),

比如去争夺锁,但这并不是故事的全部,线程间也会有协作机制。就好比在公司里你和你的

同事们,你们可能存在在晋升时的竞争,但更多时候你们更多是一起合作以完成某些任务。

    就是在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完

他们的指定代码过后再将其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使

用 notifyAll()来唤醒所有的等待线程。

wait/notify 就是线程间的一种协作机制。

方法

等待唤醒机制就是用于解决线程间通信的问题的,使用到的3个方法的含义如下:

  1. wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时 的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象 上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中

  2. notify:则选取所通知对象的 wait set 中的一个线程释放;例如,餐馆有空位置后,等候就餐最久的顾客最先 入座。

  3. notifyAll:则释放所通知对象的 wait set 上的全部线程。

    如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE 状态;

    否则,从 wait set 出来,又进入 entry set,线程就从 WAITING 状态又变成 BLOCKED 状态

2.举例:生产者与消费者问题

问题

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

代码演示

包子资源类
public class BaoZi {
    String pier ;
    String xianer ;
    boolean flag = false ;//包子资源 是否存在 包子资源状态
}
吃货线程类
public class ChiHuo extends Thread{
    private BaoZi bz;
    public ChiHuo(String name,BaoZi bz){
        super(name);
        this.bz = bz;
    }
    
    @Override
    public void run() {
        while(true){
            synchronized (bz){
                if(bz.flag == false){//没包子
                    try {
                         bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            System.out.println("吃货正在吃"+bz.pier+bz.xianer+"包子");
            bz.flag = false;
            bz.notify();
            }
        }
    }
}
包子铺线程类
public class BaoZiPu extends Thread {
    private BaoZi bz;
    public BaoZiPu(String name,BaoZi bz){
        super(name);
        this.bz = bz;
    }。
    
    @Override
    public void run() {
        int count = 0;
        //造包子
        while(true){
        //同步
            synchronized (bz){
                if(bz.flag == true){//包子资源 存在
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            // 没有包子 造包子
            System.out.println("包子铺开始做包子");
                if(count%2 == 0){
                // 冰皮 五仁
                bz.pier = "冰皮";
                bz.xianer = "五仁";
                }else{
                // 薄皮 牛肉大葱
                bz.pier = "薄皮";
                bz.xianer = "牛肉大葱";
                }
            count++;
            bz.flag=true;
            System.out.println("包子造好了:"+bz.pier+bz.xianer);
            System.out.println("吃货来吃吧");
            //唤醒等待线程 (吃货)
            bz.notify();
            }
        }
    }
}
测试类
public class Demo {
    public static void main(String[] args) {
        //等待唤醒案例
        BaoZi bz = new BaoZi();
        ChiHuo ch = new ChiHuo("吃货",bz);
        BaoZiPu bzp = new BaoZiPu("包子铺",bz);
        ch.start();
        bzp.start();
    }
}
执行结果
包子铺开始做包子
包子造好了:冰皮五仁
吃货来吃吧
吃货正在吃冰皮五仁包子
包子铺开始做包子
包子造好了:薄皮牛肉大葱
吃货来吃吧
吃货正在吃薄皮牛肉大葱包子
包子铺开始做包子
包子造好了:冰皮五仁
吃货来吃吧
吃货正在吃冰皮五仁包子

3.线程池

概念

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

步骤
  1. 创建线程池对象。

  2. 创建Runnable接口子类对象。(task)

  3. 提交Runnable接口子类对象。(take task)

  4. 关闭线程池(一般不做)。

4.Lambda表达式

标准格式
(参数类型 参数名称) ‐> { 代码语句 }

说明:

小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。

-> 是新引入的语法格式,代表指向动作。

大括号内的语法与传统方法体要求基本一致。

例子:

(无参数无返回)

public class Demo05InvokeCook {
    public static void main(String[] args) {
        invokeCook(() ‐> {
            System.out.println("吃饭啦!");
        });
    }
    
    private static void invokeCook(Cook cook) {
        cook.makeFood();
    }
}

(有参数有返回)

public class Demo08InvokeCalc {
    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);
    }
}

(省略格式)

public class Demo09InvokeCook {
    public static void main(String[] args) {
        invokeCook(() ‐> System.out.println("吃饭啦!"));
    }
    
    private static void invokeCook(Cook cook) {
        cook.makeFood();
    }
}
使用前提
  1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。 无论是JDK内置的 Runnable 、 Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一 时,才可以使用Lambda。

  2. 使用Lambda必须具有上下文推断。 也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

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

这篇关于我的java学习第七天的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!