Day07 线程状态、线程间通信、线程池、Lambda标准格式

线程状态

1. 概述

  1. New 新建状态
  2. Runnable 运行状态
  3. Blocked 阻塞状态
  4. Terminated 死亡状态
  5. Timed Waiting 休眠状态
  6. Waiting 永久等待状态

2. Timed Waiting(计时等待)

通常调用Thread的sleep方法实现Timed Waiting,需要注意:

  1. 进入Timed Waiting转台的一种常见方法是调用Thread的sleep方法, 单独的线程也可以调用,不一定非要有协作关系
  2. 为了让其它线程有机会执行,可以加将Thread.sleep()的调用放在线程run()之内。这样才能保证线程执行过程中会睡眠
  3. sleep与锁无关,线程睡眠到期自动苏醒,并返回到Runnable(可运行)状态

Tips:sleep中指定的时间是线程不运行的最短时间。sleep()方法不能保证线程休眠到期后就马上执行
在这里插入图片描述

3. Blocked(锁阻塞)

举例,线程A与线程B代码中使用同一锁,如果线程A获取到锁,线程A进入到Runnable状态,那么线程B就进入到Blocked锁阻塞状态。
在这里插入图片描述

4. Waiting(无限等待)

/*
    创建一个顾客线程:调用wait()方法,放弃cpu的执行,进入到WAITING状态
    创建一个老板线程:调用notify()方法,唤醒顾客线程
 */
public class Demo01WaitAndNotify {
    public static void main(String[] args) {
        Object obj = new Object();

            //创建一个顾客线程
            Thread t1 = new Thread() {
                @Override
                public void run() {
                    System.out.println("顾客来了");
                    synchronized (obj) {
                        System.out.println("顾客说出要的包子的种类");
                        try {
                            obj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("包子到手了,开吃!!!");
                    }
                }
            };

            //创建一个老板线程
            Thread t2 = new Thread() {
                @Override
                public void run() {
                    System.out.println("老板做包子了");
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj) {
                        System.out.println("老板花了5s中做好了包子");
                        obj.notify();
                    }
                }
            };
            Thread t3 = new Thread() {
                @Override
                public void run() {
                    System.out.println("线程3");
                }
            };
        t1.start();
        t2.start();
        t3.start();
    }
}

一个调用了某个对象的 Object.wait 方法的线程会等待另一个线程调用此对象的Object.notify()方法 或 Object.notifyAll()方法。
waiting状态并不是一个线程的操作,它体现的是多个线程间的通信,可以理解为多个线程之间的协作关系,多个线程会争取锁,同时相互之间又存在协作关系。
在这里插入图片描述

等待唤醒机制

1. 线程间通信

概念:多个线程在处理同一个资源,但是线程的任务却不相同。
Why?:多个线程并发执行时,在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。
How?:多个线程在处理同一个资源时,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。就是多个线程在操作同一份数据时,避免对同一共享变量的争夺。也就是我们需要通过一定的手段使各个线程能有效的利用资源。而这种手段就是等待唤醒机制

2. 等待唤醒机制

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

  1. wait():线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中。
  2. notify():则选取所通知对象的 wait set 中的一个线程释放;例如,餐馆有空位置后,等候就餐最久的顾客最先入座。
  3. notifyAll():则释放所通知对象的 wait set 上的全部线程。
    细节
    1… wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
  4. wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
  5. wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。
    代码:
    包子资源类:
public class BaoZi {
    String pi;
    String xian;
    boolean flag = false;
}

吃货线程类:

public class ChiHuo extends Thread {
    private BaoZi bz;

    public ChiHuo(BaoZi bz) {
        this.bz = bz;
    }

    @Override
    public void run() {
        synchronized (bz) {
            while (true) {
                if (bz.flag == false) {
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println("吃货开始吃" + bz.pi + bz.xian + "包子!!!");

                bz.flag = false;
                bz.notify();
                System.out.println("吃货把" + bz.pi + bz.xian + "包子吃完了,叫包子铺生产包子!!!");
                System.out.println("==================");
            }
        }
    }
}

包子铺线程类:

public class BaoZiPu extends Thread {
    int count = 0;

    private BaoZi bz;


    public BaoZiPu(BaoZi bz) {
        this.bz = bz;
    }

    @Override
    public void run() {
        synchronized (bz) {
            while (true) {
                if (bz.flag == true) {
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //被唤醒后执行
                if (count % 2 == 0) {
                    bz.pi = "薄皮";
                    bz.xian = "三鲜馅";
                } else {
                    bz.pi = "冰皮";
                    bz.xian = "牛肉馅";
                }
                count++;
                System.out.println("包子铺正在做" + bz.pi + bz.xian + "包子");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                bz.flag = true;
                bz.notify();
                System.out.println("包子铺做好了" + bz.pi + bz.xian + ",吃货可以吃了!!!");
            }
        }
    }
}

测试类:

public class Demo02Main {
    public static void main(String[] args) {
        BaoZi bz = new BaoZi();
        new BaoZiPu(bz).start();
        new ChiHuo(bz).start();
    }
}

线程池

1. 线程池概念

线程池:线程池就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无须反复创建多个多个线程而消耗过多资源。
线程池的工作原理:
线程池的工作原理图
合理利用线程池带来的好处:

  1. 降低资源消耗
  2. 提高响应速度
  3. 提高线程的可管理性

2. 线程池的使用

java.util.concurrent.Executors:线程池的工厂类,用来生成线程池
Executors类中的静态方法:
static ExecutorService newFixedThreadPool (int nThreads) 创建一个可重用固定线程数的线程池
参数:new nThreads:创建线程池中包含的线程数量
返回值:ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接受
java.util.concurrent.ExecutorService:线程池接口
用来从线程池中获取线程,调用start方法,执行线程任务的方法
submit(Runnable task) 提交一个Runnabale任务用于执行
关闭/销毁线程池的方法
void shutdown()
线程池的使用步骤:

  1. 使用线程池工厂的Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
  2. 创建一个类,实现Runnable接口,重写run方法,设置线程任务
  3. 调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
  4. 调用ExecutorService中的方法shutdown销毁线程池

Runnable实现类代码:

public class RunnableImpl implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行!");
    }
}

线程池测试类:

public class Demo01ThreadPool {
    public static void main(String[] args) {
        ExecutorService es = Executors.newFixedThreadPool(2);

        es.submit(new RunnableImpl());
        es.submit(new RunnableImpl());
        es.submit(new RunnableImpl());
        es.submit(new RunnableImpl());


        es.shutdown();
    }
}

Lambada表达式

1. Lambda表达式

标准格式

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

格式说明

  • 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
  • ->是新引入的语法格式,代表指向动作。
  • 大括号内的语法与传统方法体要求基本一致。
  1. 无参无返回实例
    接口类代码:
public interface Cook {
    void makeFood();
}

测试类代码:

public class Demo04Main {
    public static void main(String[] args) {

        invokeCook(new Cook(){
            @Override
            public void makeFood() {
                System.out.println("做饭了!");
            }
        });

        //使用Lambda接口优化书写
        invokeCook(()->{
            System.out.println("做饭了!");
        });

        //优化省略Lambda接口
        invokeCook(()->System.out.println("做饭了!"));
    }

    public static void invokeCook(Cook cook){
        cook.makeFood();
    }
}
  1. 有参无返回实例
    接口类代码:
public class Person {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", 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;
    }

    public Person() {
    }

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

测试类代码:

public class Demo01Arrays {
    public static void main(String[] args) {
        Person[] arr = {
          new Person("Mary",18),
          new Person("Tom",23),
          new Person("Love",19),
          new Person("Able",17)
        };

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

        //优化省略Lambda接口
        Arrays.sort(arr,(o1, o2)->o1.getAge()-o2.getAge());

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

  1. 有参有返回实例
    接口类代码:
public interface Calculator {
    int calc(int a,int b);
}

测试类代码:

public class Demo06Main {
    public static void main(String[] args) {
//        invokeCalc(10, 20, new Calculator() {
//            @Override
//            public int calc(int a, int b) {
//                return a + b;
//            }
//        });

        invokeCalc(20, 30, (a, b) -> {
            return a + b;
        });

        //优化省略Lambda接口
        invokeCalc(20, 30, (a, b) -> a + b);

    }

    public static void invokeCalc(int a, int b, Calculator c) {
        int sum = c.calc(a, b);
        System.out.println(sum);
    }
}

2. Lambda省略格式

省略规则

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

3. Lambda的使用前提

  1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法
  2. 使用Lambda必须具有上下文推断
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值