线程池、死锁、线程状态、等待与唤醒、Lambda表达式、Stream流

线程池、死锁、线程状态、等待与唤醒、Lambda表达式、Stream流

第一章 线程池

5.1 线程池的思想
当我们需要创建大量线程时,每次线程的创建和销毁都需要消耗一定性能,频繁创建和销毁肯定会降低系统效率
那么我们就可以事先创建好一堆线程,保存到某个容器中,需要使用线程时,从线程的容器中获取即可    
5.2 线程池介绍
线程池:其实就是一个容纳多个线程的容器,并且线程池中线程执行完一个任务之后,线程不销毁,从新返回到线程池中,以便下次使用
    
线程池的好处:
a.节省频繁创建和销毁线程的过程,提高了系统的性能
b.根据系统的配置,安排适当的线程数量,不会导致系统压力过大而宕机    
c.由于事先已经创建好了很多线程,提高用户的响应速度    
5.3 线程池的使用
线程池最最最顶层的根接口:
	java.util.concurrent.Executor 
根接口的子接口:
	java.util.concurrent.ExecutorService
工具类,专门帮助我们创建线程池对象:
	java.util.concurrent.Executors
静态方法:
	public static ExecutorService newFixedThreadPool(int nThreads); 创建一个线程数固定的线程池
有了线程池之后,我们只需要向线程池中提交任务即可!!!
    public Future<?> submit(Runnable task);向线程池中提交一个任务,返回结果是null     
    public Future<?> submit(Callable<T> task);向线程池中提交一个任务,有返回值,返回Future对象    
5.4 线程池的练习
public class ThreadPoolDemo {
    public static void main(String[] args) {
        //1.创建一个线程池对象
        ExecutorService service = Executors.newFixedThreadPool(3);
        //2.提交无返回值的任务
        for (int i = 0; i < 10; i++) {
            service.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "执行了...");
                }
            });
        }
        //3.提交有返回值的任务
        Future<Integer> future = service.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws InterruptedException {
                    int sum = 0;
                    for (int i = 1; i < 101; i++) {
                        Thread.sleep(50);
                        sum += i;
                    }
                    return sum;
                }
            });
            System.out.println("future对象得到了..");
            //通过future对象获取结果
            Integer result = future.get();
            System.out.println("从future对象中获取任务结果..");
            System.out.println("执行结果:"+result);
    	}
    }
}

第二章 死锁

6.1 什么是死锁
两个线程,执行同一个任务,但是需要获取两次锁对象才能执行任务,
当线程1获取第一个锁,当线程2获取第二个锁,线程1会等待第二个锁,线程2会等待第一锁,此时的现象就称为死锁
6.2 产生死锁的条件
a.至少2个两线程
b.至少2把锁对象
c.获取锁需要嵌套(反向嵌套)    
6.3 死锁演示
public class DeadLockDemo {
    public static void main(String[] args) {
        //1.2个锁对象
        Object obj1 = new Object();
        Object obj2 = new Object();
        //2.两个线程池
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (obj1) {
                        System.out.println("线程1获取了锁对象obj1,等待获取锁对象obj2");
                        synchronized (obj2) {
                            System.out.println("线程1的任务执行了...");
                        }
                    }
                }

            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (obj2) {
                        System.out.println("线程2获取了锁对象obj2,等待获取锁对象obj1");
                        synchronized (obj1) {
                            System.out.println("线程2的任务执行了...");
                        }
                    }
                }
            }
        }).start();
    }
}
注意:我们应该尽量避免死锁

第三章 线程的状态

1.线程的六种状态
  • 新建状态(New)

    刚刚创建的线程且没有调用start方法
    
  • 可运行状态(Runnable)

    处于新建状态的线程,调用start方法 
    
  • 受(锁)阻塞状态(Blocked)

    当前线程需要锁对象,但是锁对象被其他线程持有
    
  • 限时等待状态(Timed_waiting)

    在线程任务中调用Thread.sleep(毫秒值)
    
  • 无限等待状态(Waiting)

    • 线程如何进入Waiting(无线等待状态)

      a.当前线程必须先持有锁对象
      b.调用锁对象.wait()方法,进入无限等待
      c.进入无限等待之前,线程会自动释放锁
      
    • 其他线程如何唤醒Waiting状态的线程

      a.其他线程持有锁对象(必须是无限等待线程释放的那个锁对象)
      b.调用锁对象.notify()方法,唤醒无限等待的线程
      c.被唤醒的线程进入锁阻塞状态,直到再次抢到锁    
      
  • 消亡状态(Terminated)

    线程的任务执行完毕,此时线程就会进入消亡
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fpZQMn3e-1591441676737)(img/image-20191228101138166.png)]

第四章 等待唤醒机制

1.等待唤醒机制(Wait和Notify)
public class WaitAndNotifyDemo {
    public static void main(String[] args) throws InterruptedException {
        //0.创建一个锁对象
        Object obj = new Object();

        //1.创建一个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程1即将获取锁obj..");
                synchronized (obj){
                    System.out.println("线程1获取到了锁obj...");
                    System.out.println("线程1进入了无限等待...");
                    try {
                        obj.wait(); //进入无限等待,并自动释放锁对象
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程1从无限等待中醒来了..");
                }
            }
        }).start();

        for (int i = 0; i < 10; i++) {
            Thread.sleep(500);
            System.out.println(i);
        }
        //2.在创建一个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程2来了..");
                synchronized (obj) {
                    System.out.println("线程2获取到了obj锁...");
                    System.out.println("线程2把线程1唤醒了...");
                    obj.notify();
                    //这个循环的目的: 让线程2唤醒线程1之后,不要立刻释放锁
                    //此时线程1醒来了,但是由于没有锁对象,进入锁阻塞状态
                    //线程2数完10下之后,才释放锁,线程1才能再次获取锁进入运行状态
                    for (int i = 0; i < 10; i++) {
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(i);
                    }
                }
            }
        }).start();
    }
}
2.生产者与消费者问题(代码演示)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h1IieWmF-1591441676738)(img/image-20191228113820866.png)]

//盘子类,里面有包子
public class PanZi {
    boolean flag = true;// true代表有包子 false 没有包子
}
public class ProducerAndCustomerDemo {
    public static void main(String[] args) {
        //1.创建一个盘子
        PanZi pz = new PanZi();
        Object obj = new Object();
        //2.生产者线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    //加锁
                    synchronized (obj) {
                        //判断
                        if (pz.flag == true) {
                            //进入WAITING
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        //做包子
                        pz.flag = true;
                        System.out.println("生产者的包子做好了...");
                        //唤醒消费者
                        System.out.println("吃货来吧...");
                        obj.notify();
                    }
                }
            }
        }).start();

        //3.消费者线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    //加锁
                    synchronized (obj) {
                        //判断
                        if (pz.flag == false) {
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        //吃包子
                        pz.flag = false;
                        System.out.println("包子真好吃....");
                        //唤醒生产者
                        System.out.println("再给哥做一个....");
                        obj.notify();
                    }
                }
            }
        }).start();
    }
}

第五章 Lambda表达式

2.1 函数式编程的思想
只强调输入量,计算过程和输出量,而不强调其他语法形式
2.2 冗余的Runnable代码
public class LambdaDemo {
    public static void main(String[] args) {
        //1.使用传统的面向对象思想,开启线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"执行了..");
            }
        }).start();
        //分析:以上代码的冗余之处
        /**
         * a.由于面向对象语法的要求,我们不得不创建Runnable的实现类
         * b.由于面向对象语法的要求,我们不得不重写run方法(和接口中的run必须一模一样)
         * c.除了任务代码,其他似乎都是面向对象逼我的,真正我心甘情愿就是任务代码
         */
    }
}
2.3 函数式编程Lambda的体验
Lambda思想: 强调输入量(参数)-->计算过程(方法体)-->输出量(返回值)
//使用Lambda可以替代匿名内部类
new Thread(()->{System.out.println(Thread.currentThread().getName()+"执行了..");}).start();    
2.4 Lambda标准格式介绍
Lambda的标准格式:
	(参数列表)->{方法体;return 返回值;}
	参数列表: 输入量(格式: 数据类型 参数名,数据类型 参数,...)
    方法体: 计算过程(一句或者多句代码)
    return语句: 输出量 (return 结果;)   
2.5 Lambda的参数和返回值
public class LambdaDemo {
    public static void main(String[] args) {
        //1.创建一个数组
        Integer[] arr = {3, 4, 678, 1, 5, 67, 33};
        //2.排序
//        Arrays.sort(arr, new Comparator() {
//            @Override
//            //口诀: 升序 前-后
//            public int compare(Integer o1, Integer o2) {
//                return o2-o1;
//            }
//        });
        //使用Lambda替代匿名内部类
        Arrays.sort(arr,(Integer o1, Integer o2)->{return o2-o1;});
        //3.打印
        System.out.println(Arrays.toString(arr));
    }
}
public class Dog {
    private int age;
    private String name;
    private int legs;
    //..其他省略
}

public class LambdaDemo01 {
    public static void main(String[] args) {
        //1.创建一个数组
        ArrayList<Dog> dogs = new ArrayList<Dog>();
        //2.添加数据
        dogs.add(new Dog(2, "旺财", 3));
        dogs.add(new Dog(3, "来福", 2));
        dogs.add(new Dog(1, "哮天犬", 4));
        dogs.add(new Dog(5, "大黄", 5));
        //3.对集合进行排序
//        Collections.sort(dogs, new Comparator() {
//            @Override
//            public int compare(Dog o1, Dog o2) {
//                //按照狗的腿数升序排序
//                return o1.getLegs()-o2.getLegs();
//            }
//        });
        //使用Lambda替代匿名内部类
        Collections.sort(dogs,(Dog o1, Dog o2)->{return o1.getLegs()-o2.getLegs();});
        //4.打印
        for (Dog dog : dogs) {
            System.out.println(dog);
        }
    }
}
2.6 Lambda的省略格式
a.参数列表中数据类型可以省略
b.如果参数只有一个,那么小括号可以省略
c.如果方法体和返回值可以写成一句代码,那么{},return,;可以同时省略    
    
//使用Lambda可以替代匿名内部类
new Thread(()->{System.out.println(Thread.currentThread().getName()+"执行了..");}).start();
//使用Lambda的省略格式
new Thread(()->System.out.println(Thread.currentThread().getName()+"执行了..")).start();    

//使用Lambda替代匿名内部类
Arrays.sort(arr,(Integer o1, Integer o2)->{return o2-o1;});
//使用Lambda的省略格式
Arrays.sort(arr, (o1, o2) -> o2 - o1);


//使用Lambda替代匿名内部类
Collections.sort(dogs,(Dog o1, Dog o2)->{return o1.getLegs()-o2.getLegs();});
//使用Lambda的省略格式
Collections.sort(dogs, (o1, o2) -> o1.getLegs() - o2.getLegs());
2.7 强烈注意:Lambda的使用前提
a.Lambda只能用于替代函数式接口的匿名内部类
    	函数式接口:有且只有一个抽象方法的接口
            
b.Lambda的省略格式只有以上三种,其他均不可省略(可推导即可省略)            

第六章 Stream流

1.引入:传统的集合操作
/**
 * 传统的集合遍历
 */
public class StreamDemo01 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
//        1. 首先筛选所有姓张的人;
        List<String> one = new ArrayList<String>();
        for (String s : list) {
            if (s.startsWith("张")) {
                one.add(s);
            }
        }
//        2. 然后筛选名字有三个字的人;
        List<String> two = new ArrayList<String>();
        for (String s : one) {
            if (s.length() == 3) {
                two.add(s);
            }
        }
//        3. 最后进行对结果进行打印输出。
        for (String s : two) {
            System.out.println(s);
        }
    }
}
2.循环遍历的弊端分析
a.注重形式(面向对象语法的束缚)
    每次都需要定义集合
    每次都需要遍历集合    
3.Stream的优雅写法
list.stream().filter(s->s.startsWith("张")).filter(s->s.length()==3).
    forEach(s-> System.out.println(s));
4.流式思想的概述
流式思想:Stream流式思想   

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mw6oq4tU-1591441676739)(img/image-20191228160714763.png)]

5.两种获取流的方式
集合获取流:
	调用集合的一个方法:stream()
    
数组获取流:
	Stream.of(数组)
        
/**
 * 容器如何获取流
 */
public class StreamDemo02 {
    public static void main(String[] args) {
        //1.集合获取流
        ArrayList<String> arr1 = new ArrayList<String>();
        Stream<String> stream1 = arr1.stream();

        HashSet<String> set1 = new HashSet<String>();
        Stream<String> stream2 = set1.stream();

        //双列集合无法直接获取流(了解即可)
//        HashMap map = new HashMap();
//        Stream stream3 = map.keySet().stream();
//        Stream> stream4 = map.entrySet().stream();
        //2.数组获取流
        Integer[] arr = new Integer[10];
        Stream<Integer> stream = Stream.of(arr);
    }
}        
6.Stream流中的常用方法
//1.获取一个Stream对象
Stream<String> stream = Stream.of("jack", "rose", "bob", "james", "tom", "jerry");
  • 逐个处理:forEach(代码演示)

    //2.foreach 逐一遍历
    stream.forEach(new Consumer<String>() {
        @Override
        public void accept(String s) {
            System.out.println(s);
        }
    });
    //使用Lambda替代匿名内部类
    stream.forEach((String s)->{System.out.println(s);});
    //省略格式
    stream.forEach(s -> System.out.println(s));
    
  • 统计个数:count(代码演示)

    //3.count 统计个数
    long count = stream.count();
    System.out.println("流中有"+count+"个元素");
    
  • 过滤:filter(代码演示)

    //4.filter 过滤方法
    Stream<String> stream1 = stream.filter(new Predicate<String>() {
        @Override
        public boolean test(String s) {
            //只要长度为5的元素
            return s.length() == 5;
        }
    });
    //使用Lambda替换匿名内部类
    Stream<String> stream1 = stream.filter((String s)->{return s.length() == 5;});
    //省略格式 
    Stream<String> stream1 = stream.filter(s-> s.length() == 5);
    stream1.forEach(s -> System.out.println(s));
    
  • 取前几个:limit(代码演示)

    //5.limit 取前几个
    Stream<String> stream1 = stream.limit(3);
    stream1.forEach(s -> System.out.println(s));
    
  • 跳过前几个:skip(代码演示)

    //6.skip 跳过前几个
    Stream<String> stream1 = stream.skip(2);
    stream1.forEach(s -> System.out.println(s));
    
  • 映射方法:map(代码演示)

    //7.map 映射方法
    Stream<Integer> stream1 = stream.map(new Function<String,Integer>() {
        @Override
        public Integer apply(String s) {
            return s.length();
        }
    });
    //使用Lambda代替匿名内部类
    Stream<Integer> stream1 = stream.map((String s)->{return s.length();});
    //使用Lambda的省略格式
    Stream<Integer> stream1 = stream.map(s -> s.length());
    stream1.forEach(i -> System.out.println(i));
    
  • 静态方法合并流:concat(代码演示)

    Stream的静态方法:合并两个流
    //8.concat 合并流
    Stream<String> one = Stream.of("jack","rose");
    Stream<String> two = Stream.of("bob", "tom");
    //合并
    Stream<String> all = Stream.concat(one, two);
    //foreach
    all.forEach(s -> System.out.println(s)); 
    
    问题1:
    	如果有三个流,怎么合并? 先合并两个,再和第三个合并
    问题2:
    	合并的这个两个流的泛型有没有要求呢????
            没有!!! 合并之后的流的泛型可以写两个流泛型的共同父类
    		Stream<Double> one1 = Stream.of(1.1,2.2,3.3);
            Stream<Integer> two1 = Stream.of(10, 20);
            Stream<Number> all1 = Stream.concat(one1,two1);
    
7.练习:集合元素的处理(Stream方式)
public class StreamDemo04 {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("老子");
        one.add("庄子");
        one.add("孙子");
        one.add("洪七公");
        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("张三丰");
        two.add("赵丽颖");
        two.add("张二狗");
        two.add("张天爱");
        two.add("张三");
//        1. 第一个队伍只要名字为3个字的成员姓名;
//        2. 第一个队伍筛选之后只要前3个人;
        Stream<String> streamOne = one.stream().filter(s -> s.length() == 3).limit(3);
//        3. 第二个队伍只要姓张的成员姓名;
//        4. 第二个队伍筛选之后不要前2个人;
        Stream<String> streamTwo = two.stream().filter(s -> s.startsWith("张")).skip(2);
//        5. 将两个队伍合并为一个队伍;
        Stream<String> streamNames = Stream.concat(streamOne, streamTwo);
//        6. 根据姓名创建 Person 对象,映射
        Stream<Person> personStream = streamNames.map(s -> new Person(s));
//        7. 打印整个队伍的Person对象信息
        personStream.forEach(p-> System.out.println(p));

//		大神专用,我们了解即可
//        Stream.concat(
//                one.stream().filter(s -> s.length() == 3).limit(3),
//                two.stream().filter(s -> s.startsWith("张")).skip(2)
//        ).map(s -> new Person(s))
//                .forEach(p -> System.out.println(p));
    }
}
8.总结:函数拼接和终结方法
函数拼接方法(模型拼接方法)
    如果调用方法之后,返回的还是流对象,此方法称为函数拼接方法
    filter方法
    limit方法
    skip方法
    map方法
    concat方法
    所有的函数拼接方法支持链式编程!!!! 
终结方法
    如果调用方法之后,返回的不是流对象或者没有返回值,此方法称为终结方法
    foreach方法
    count方法  
    所有的总结方法,不支持链式编程,一个流只能调用一次终结方法,调用完毕之后流会关闭
9.收集Stream的结果
调用流对象的collect方法
//9.收集流的结果
//收集到List集合中
List<String> list = stream.collect(Collectors.toList());
System.out.println(list);
//收集到Set集合中
Set<String> set = stream.collect(Collectors.toSet());
System.out.println(set);
//收集到数组中
Object[] objs = stream.toArray();
System.out.println(Arrays.toString(objs));
总结:
能够描述Java中线程池运行原理
能够描述死锁产生的原因
"能够说出线程6个状态的名称,并且能够描述他们之间的转换
"能够理解等待唤醒案例(基本案例,吃包子案例)
"能够掌握Lambda表达式的标准格式与省略格式
"能够通过集合、映射或数组方式获取流
"能够掌握常用的流操作
======"只要我们完成最后一个Stream流的综合即可"==========    
能够将流中的内容收集到集合和数组中

你可能感兴趣的:(学习记录)