Java SE学习总结 Day (26)

Day 26开篇:
      
       "今天java基础主要学习了线程通信,Lambda表达式,Stream等"



知识点反馈:

今天的知识点总结的思维导图

一.线程通信

1. Lock锁、synchronized的对比

(1)Lock是一个显示锁(锁开跟锁关都需要手动进行操作,别忘记了释放锁资源)

(2)synchronized是一个隐式锁,完全靠作用域操作,不需要手动管理

(3)Lock只有代码块锁,synchronized既有代码块也有方法锁

(4)Lock的性能比较好,Jvm只需要花比较少的时间来调用代码,而且扩展性也比较强(Lock是一个接口,接口可以多实现)

如果是简单的同步代码块,可以优先考虑Lock,如果是同步方法只能使用synchronized但是在实际的开发当中,synchronized的使用场景还是比Lock高,因为Lock是一个新特性。

 

扫描二维码关注公众号,回复: 9432852 查看本文章

 2.一个类似的案例(你们熟悉的学生对象)

    资源:学生对象Student

    获取线程:GetStudent

    设置线程:SetStudent

案例产生问题:

(1)每次获取的数据都是null---0---/u00000

            原因:是因为获取和设置两个线程分别使用了不同的Student对象

            解决:创建一个Student的栈空间,不进行赋值,交给构造器 要求调用者进行赋值堆空间

(2)数据获取的不完整,甚至造成了数据错乱

            原因:因为有可能设置线程并没有抢到执行权,所有获取线程就拿不到数据,也有可能设置或者获取线程执行过程当中,相互抢夺了,所以造成数据错乱

            解决:给两个线程,都加上synchronize同步锁,至少可以保证两个线程在执行过程当中不会被打断,从而造成数据错乱的问题

 

            为了保证数据的真实准确,我们加入死循环不断生产和消费数据(事实证明,数据错乱的问题,已经解决好了)

 

 (3)读到的数据仍然可能是null---0---/u0000

            原因:设置线程并没有从第一次就抢到执行权,所有获取线程拿到的就是空数据

            

3.何谓线程通信?

 (1)其实就是两个线程之间,产生一个互相连接通知的过程。

 (2)void wait():使当前线程挂起并放弃CPU的执行权,同步资源并等待,使其他线程可以访问并修改共享资源,wait一旦被执行必须调用

(3)notify或者notifyAll进行唤醒,唤醒后在就绪位置重新进行CPU的抢夺。

 (4)notify():唤醒正在排队(阻塞状态)等待同步资源线程中优先级别最高的线程进行操作。

 (5)notifyAll():唤醒所有正在排队(阻塞状态)等待同步线程的资源

(6)为什么wait/notify不是在Thread里面,反而跑到Object里面?

       wait/notify进行操作的前提一定是基于一个同步监视器(对象锁)来完成。而对象锁又可以是任何的锁对象(任意类都没问题),所有的类都是Object的子类,所以放到Object里面避免类型接收和转换的问题。

 

 (7)notify:是唤醒线程,只不过是把线程从一个阻塞状态调整为就绪状态,所以不保证一定执行,只能有抢夺执行权的机会。

 

这个案例的整个编码的过程,其实就是一个非常经典的生产者和消费者设计模式。

 

例子:

public class Demo {

    public static void main(String[] args) {

        //保证get和set两个线程获取的是同一个资源

        Student s = new Student();

        GetStundet gs = new GetStundet(s);

        SetStudent ss = new SetStudent(s);

        //创建设置Thread

        Thread t1 = new Thread(ss);

        //创建获取Thread

        Thread t2 = new Thread(gs);

        t2.start();

        t1.start();

    }

}

二.Lambda表达式

1.JDK8的介绍:

(1) java 8,是java语言再1.5版本之后更新最大的一个版本。是2014年3年发布可以看做是能够和java5并肩的一个重量级版本

(2)特性:

            速度更快

            代码更少(新增的新的语法:Lambda表达式)

            非常的强大的StreamAPI

            最大的减少了空指针异常的操作:Optional(谷歌写着玩)

            Nashorm引擎,运行JVM当中操作JavaScrpit代码

2. 为什么要去使用Lambda表达式?

 Lambda其实本质上就是一个匿名函数,可以将Lambda表达式理解成一个一段可以进行执行传递的代码,他可以写出更加简洁,更加灵活的代码,是一个非常紧凑型的代码分割。

3.Lambda的格式解释:

         () -> {

            System.out.println("我是一个Lambda");

         };

(1) ->:lambda的操作符号(箭头操作符)

(2)左边数据:lambda的形参列表(其实就你重新接口那个抽象方法的形参列表)

(3)右边数据:lambda的方法体(其实就是你重写那个接口具体方法体)

4. lambda只能用于接口?

是的,而且只能用于函数式接口(整个接口当中只存一个抽象方法)

5.lambda是怎么找到这方法?

因为只能用于存在一个抽象方法的接口,所以完全采用了自动推断机制进行推断

6.lambda的具体使用(看下面例子代码)

7.关于省略的问题:

(1)左边:所有的形参的数据类型可以直接省略,实行自动推断。如果形参列表只有一个的时候,甚至连括号都可以直接省略掉

(2)右边:当语句体里面值存在一句代码的时候,可以省略大括号,甚至可以连return省略掉

8.啥时候用它呢?

凡是可以使用内部类实现的,都可以使用lambda表达式(这个接口有且只有一个抽象)

例子:

 package lambda;

import java.util.Comparator;

import java.util.function.Consumer;

public class LambdaTest02 {

    public static void main(String[] args) {

        //test01();

        //test02();

        //test03();

        //test04();

        //当你的lambda表达式,语句体里面只存在一句代码的时候,可以将大括号、return都直接省略

        Comparator<Integer> com = new Comparator<Integer>() {

            @Override

            public int compare(Integer o1, Integer o2) {

                return o1+o2;

            }

        };

        System.out.println(com.compare(2,1));

        System.out.println("--------------");

        Comparator<Integer> com2 = (o1,o2) -> o1+o2;

        System.out.println(com2.compare(2,1));

    }

    public static void test04() {

        Comparator<Integer> com = new Comparator<Integer>() {

            @Override

            public int compare(Integer o1, Integer o2) {

                return o1+o2;

            }

        };

        System.out.println(com.compare(2,1));

        System.out.println("--------------");

        Comparator<Integer> com2 = (o1,o2) -> {

            System.out.println(o1);

            System.out.println(o2);

          return o1+o2;

        };

        System.out.println(com2.compare(2,1));

    }

    public static void test03() {

        //语法格式三:使用Lambda的时候,所有的参数列表的数据类型可以省略,因为完全实现自动推断

        Consumer<String> con = new Consumer<String>() {

            @Override

            public void accept(String s) {

                System.out.println(s);

            }

        };

        con.accept("这个世界上最美的女人");

        System.out.println("---------------");

        //Consumer con1 = (s) -> System.out.println(s);

        //当你的形参只有一个的时候,连形参括号都可以直接省略

        Consumer con1 = s -> System.out.println(s);

        con1.accept("小花");

    }

    public static void test02() {

        //语法格式二:一个形式参数,没有返回值

        Consumer<String> con = new Consumer<String>() {

            @Override

            public void accept(String s) {

                System.out.println(s);

            }

        };

        con.accept("这个世界上最美的女人");

        System.out.println("---------------");

        Consumer<String> con1 = (String s) -> {

            System.out.println(s);

        };

        con1.accept("小花");

    }

    public static void test01() {

        //语法格式一:方法无参也无返回值

        Runnable r1 = new Runnable() {

            @Override

            public void run() {

                System.out.println("我是一个内部类");

            }

        };

        r1.run();

        System.out.println("---------------");

        Runnable r2 = () -> {

            System.out.println("我是一个Lambda");

        };

        r2.run();

    }

}

9. 方法引用:

(1)使用场景:当要传递给Lambda体的操作,已经有实现的方法,就可以使用方法引用。

(2)具体介绍:本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例,所以方法引用就是函数数接口实例。一样。

(3)格式:

        类(具体对象) :: 方法名(必须是成立的)

例子:

import java.io.PrintStream;

import java.util.function.Consumer;

import java.util.function.Supplier;

public class MethodRef {

    public static void main(String[] args) {

        //test01();

        Student s = new Student("toobug", 19);

        Supplier<String> sup = () -> s.getName();

        System.out.println(sup.get());

        System.out.println("-------------");

        Supplier<String> sup1 = s::getName;

        System.out.println(sup1.get());

    }

    public static void test01() {

        Consumer<String> con = new Consumer<String>() {

            @Override

            public void accept(String s) {

                System.out.println(s);

            }

        };

        con.accept("TOOBUG");

        System.out.println("-----------------");

        Consumer<String> con1 = (String str) -> System.out.println(str);

        con1.accept("TOOBUG");

        System.out.println("-----------------");

        PrintStream ps = System.out;

        Consumer<String> con2 = ps::println;//自动推断

        con2.accept("toobug");

    }

}

三.Stream

1.Java8有两大重点特性:Lambda和StreamAPI

2.StreamAPI:是真正将一个函数式编程的风格引入到Java当中的一种技术,可以让程序员写出更加高效、干净简洁的代码

3.Stream是Java8当中处理集合的关键抽象概念,可以对你指定的集合进程非常负载的操作,比如集合的查找、过滤、映射数据等操作,使用Stream可以对复杂数据进行操作,就有点类似于SQL执行数据操作一样。

4.Stream和Collection集合的区别是什么?

(1)Collection是一种静态的内存数据结构。

(2) Stream是一个面向CPU的计算结构。

5.Stream特点:

(1)Stream本身不会存储数据(数据从集合过来,和迭代器一样)

  1. Stream本身并不会改变源数据,但是他会返回一个持有将结果的新的Stream对象

(3)Stream是延时的,只有在你关闭的时候才会同一返回数据(类似缓冲流)

6.Stream创建的四个步骤:

(1)准备一个数据源(可以是集合,也可以是数组)

(2)获取一个流

(3)自己进行中间操作链(其实就是对集合进行处理的一大堆步骤)

(4)终止操作

7.问题:获取流

java8当中,其实对Collection的接口进行一个扩展,这个扩展提供了两个流的获取方法:(1)顺序流:default Stream<E> stream;

         (2)并行流:default Stream<E> parallelStream;

例子:

import java.util.Arrays;

import java.util.Collection;

import java.util.List;

import java.util.stream.Stream;

public class StreamAPIDemo {

    public static void main(String[] args) {

        //准备好数据源

        List<String> list = Arrays.asList("小马","小明","小军","小东");

        //获取集合的顺序流

       Stream<String> stream =  list.stream();

        System.out.println(stream);

        //获取一个并行流

        Stream<String> stream1 = list.parallelStream();

        System.out.println(stream1);

        //获取所有偶数,并且输出

        //无限流(无止境生产数据)

        Stream.iterate(0,t -> t + 2).forEach(System.out::println);

    }

}

 

 

 

发布了35 篇原创文章 · 获赞 7 · 访问量 3895

猜你喜欢

转载自blog.csdn.net/weixin_45406656/article/details/104361427
今日推荐