day18【Callable开启多线程、死锁、线程状态、Lambda】课上

1.实现多线程的第三种方式_Callable开启多线程(理解)

在java中实现多线程有三种方式:

  • 继承Thread类 重写Thread类中的run方法,自定义类称为线程类

  • 自定义类实现任务接口Runnable,实现Runnable中的run方法,书写任务 自定义类称为任务类

    void run()  
    

    任务方法run没有返回值

  • 自定义类实现任务方法Callable,该接口Callable 接口类似于 Runnable,只是Runnable接口的run方法没有返回值,而Callable接口中的**call**()方法是有返回值的:

     V call() 计算结果,如果无法计算结果,则抛出一个异常。 
         	返回值:V 就是返回的结果
    
    • 实现多线程方式三实现Callable接口的步骤:

      • 自定义任务类实现任务接口Callable<V>

      • 在自定义任务类中实现抽象方法call()方法,书写任务

      • 创建任务类对象

      • 获取线程池对象,存放线程

      • 使用线程池对象调用方法:

        <T> Future<T> submit(Callable<T> task)  
            参数:
            	task:表示要执行的任务对象
             返回值:使用Future接收call方法的返回值,使用Future接口中的方法: V get() 取出结果
        
      • 代码演示:

        练习1:

        package com.itheima.sh.callable_01;
        
        import java.util.concurrent.Callable;
        
        // 1.自定义任务类实现任务接口`Callable<V>`
        public class MyTask implements Callable<Object>{
            // 2.在自定义任务类中实现抽象方法call()方法,书写任务
            @Override
            public Object call() throws Exception {
                for (int i = 0; i < 10; i++) {
                    System.out.println(i);
                }
                return null;
            }
        }
        
        package com.itheima.sh.callable_01;
        
        import java.util.concurrent.ExecutionException;
        import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        import java.util.concurrent.Future;
        
        /*
            实现多线程方式三实现`Callable`接口的步骤:
                1.自定义任务类实现任务接口`Callable<V>`
                2.在自定义任务类中实现抽象方法call()方法,书写任务
                3.创建任务类对象
                4.获取线程池对象,存放线程
                5.使用线程池对象调用方法:
                    <T> Future<T> submit(Callable<T> task)
                    参数:
                        task:表示要执行的任务对象
                     返回值:使用Future接收call方法的返回值,
                     使用Future接口中的方法: V get() 取出结果
         */
        public class Test01 {
            public static void main(String[] args) throws ExecutionException, InterruptedException {
                //3.创建任务类对象
                MyTask mt = new MyTask();
                //4.获取线程池对象,存放线程
                ExecutorService es = Executors.newFixedThreadPool(3);
                //5.使用线程池对象调用方法: <T> Future<T> submit(Callable<T> task)
                Future<Object> f = es.submit(mt);
                //6.取出返回的数据
                Object o = f.get();
                System.out.println("o = " + o);//null
            }
        }
        
        

        练习2:

        package com.itheima.sh.callable_01;
        
        import java.util.concurrent.Callable;
        
        //1.自定义任务类实现任务接口`Callable<V>`
        public class MyTask2 implements Callable<Integer>{
            //2.在自定义任务类中实现抽象方法call()方法,书写任务
        
            //需求:求1-5和值
        
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 1; i <= 5; i++) {
                    sum = sum + i;
                }
                return sum;
            }
        }
        
        package com.itheima.sh.callable_01;
        
        import java.util.concurrent.ExecutionException;
        import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        import java.util.concurrent.Future;
        
        /*
            实现多线程方式三实现`Callable`接口的步骤:
                1.自定义任务类实现任务接口`Callable<V>`
                2.在自定义任务类中实现抽象方法call()方法,书写任务
                3.创建任务类对象
                4.获取线程池对象,存放线程
                5.使用线程池对象调用方法:
                    <T> Future<T> submit(Callable<T> task)
                    参数:
                        task:表示要执行的任务对象
                     返回值:使用Future接收call方法的返回值,
                     使用Future接口中的方法: V get() 取出结果
         */
        public class Test02 {
            public static void main(String[] args) throws ExecutionException, InterruptedException {
                // 3.创建任务类对象
                MyTask2 mt = new MyTask2();
                // 4.获取线程池对象,存放线程
                ExecutorService es = Executors.newFixedThreadPool(2);
                // 5.使用线程池对象调用方法:<T> Future<T> submit(Callable<T> task)
                Future<Integer> f = es.submit(mt);
                // 6.获取数据 V get() 取出结果
                Integer i = f.get();
                System.out.println("i = " + i);
            }
        }
        
        
      • 小结:实现多线程方式三:继承Callable接口,实现方法 V call() 有返回值 只要需要返回值的就使用Callable不需要返回值就使用Runnable

2.死锁(掌握)

  • 概念:就是多个线程操作共享资源时,会产生互相等待锁的现象就是死锁。

  • 死锁是要避免的,我们平常使用多线程开发的时候会经常遇到死锁现象,所以一定要避免。

  • 演示死锁现象:

    • 如果使用同步的嵌套并且获取多把锁特别容易出现死锁现象
    • 死锁现象案例描述:有两个线程 t1 t2,执行共享资源必须获取两把锁才可以执行。
      • t1线程:获取锁的顺序:先获取lock_a锁 然后在获取lock_b锁
      • t2线程:获取锁的顺序:先获取lock_b锁 然后在获取lock_a锁
  • 代码演示:

    package com.itheima.sh.death_lock_02;
    /*
        - t1线程:获取锁的顺序:先获取lock_a锁  然后在获取lock_b锁
        - t2线程:获取锁的顺序:先获取lock_b锁  然后在获取lock_a锁
     */
    public class DeathLockTask implements Runnable {
        //定义成员变量
        private Object lock_a = new Object();
        private Object lock_b = new Object();
        boolean flag = true;//定义标记让jvm执行哪个代码
        @Override
        public void run() {
            //判断
            //- t1线程:获取锁的顺序:先获取lock_a锁  然后在获取lock_b锁
            if(flag){//假设t1执行
                while(true){
                    //加同步
                    synchronized (lock_a){//t1
                        System.out.println(Thread.currentThread().getName()+"---if----lock_a");
                        synchronized (lock_b){
                            System.out.println(Thread.currentThread().getName()+"---if----lock_b");
                        }
                    }
                }
            }else{//t2执行
                // - t2线程:获取锁的顺序:先获取lock_b锁  然后在获取lock_a锁
                while(true){
                    //加同步
                    synchronized (lock_b){//t2
                        System.out.println(Thread.currentThread().getName()+"---else----lock_b");
                        synchronized (lock_a){
                            System.out.println(Thread.currentThread().getName()+"---else----lock_a");
                        }
                    }
                }
            }
        }
    }
    package com.itheima.sh.death_lock_02;
    
    public class Test01 {
        public static void main(String[] args) throws InterruptedException {
            //创建任务类对象
            DeathLockTask dlt = new DeathLockTask();
            //创建线程
            Thread t1 = new Thread(dlt, "t1");
            t1.start();
    
            //让主线程休眠
            Thread.sleep(10);
            //修改flag标记,让t2线程执行else
            dlt.flag = false;
            //创建线程
            Thread t2 = new Thread(dlt, "t2");
            t2.start();
        }
    }
    
    
  • 图解:

在这里插入图片描述

  • 小结:
    • 开发中我们要一定避免死锁现象,能不要同步嵌套就不嵌套,如果嵌套锁保证唯一。

3.线程状态(掌握)

在java中多线程一共有六个状态。位于枚举类Thread.State中:

线程状态导致状态发生条件
NEW(新建)线程刚被创建,但是并未启动。还没调用start方法。MyThread t = new MyThread()只有线程对象,没有线程特征。
Runnable(可运行)线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。调用了t.start()方法 :就绪(经典叫法)
Blocked(锁阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting(无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
Timed Waiting(计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。
Terminated(被终止)因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。
1.New 新建状态 就是刚创建线程,还没有调用start启动  new Thread(任务对象)  不能被cpu运行
2.Runnable 可运行状态,就是使用线程调用start方法获取系统资源启动线程,但是不一定立刻被cpu执行,只是有可能被运行,在运行的队列中等待(可运行状态的线程具有cpu执行的资格)
3.Blocked(锁阻塞状态) :就是某个线程想要进入到同步中必须获取锁对象,此时锁对象被其他线程占有,二当前线程就是处于锁阻塞状态
4.Timed Waiting(计时等待)状态:有两种情况可以让一个正在运行的线程进入到计时等待状态:
    	1)线程遇到static sleep(毫秒) 此时线程就会进入到计时等待状态,进入到计时等待状态的线程不会被cpu执行,只有休眠的线程醒来之后才有可能被cpu执行(具有cpu执行的资格,在可运行的队列中等待cpu执行)
    	2)使用锁对象调用Object类中的等待方法:void wait(long timeout)  位于Object类中的原因是因为该方法必须使用锁对象调用,而锁对象是任意对象,能够被任意对象调用的方法肯定位于所有类的共同父类Object中。
    		对于带参数的wait(long timeout) 有两种醒来方式:a:时间到自然醒 b:时间未到被唤醒
    		从等待中醒来的线程必须获取到锁对象才可以被执行,没有锁对象即使醒来也不会执行
5.无限等待:使用锁对象调用Object类中的wait()是无参的可以让线程无限等待。处于无限等待的线程只能被唤醒,使用锁对象调用Object类中的  notify() 方法或 notifyAll() 来唤醒等待线程。cpu不会执行无限等待的线程
    
6.Terminated(被终止):线程停止运行。1)遇到异常,线程就会被终止  2)执行完任务

线程状态图:

在这里插入图片描述

4.线程之间的通信_等待唤醒机制(包子铺卖包子)掌握

等待唤醒机制,在这里我们使用生产者和消费者线程来进行演示。

  • 为什么等待和唤醒的方法位于Object类中?

    等待方法:void wait();

    唤醒方法:

    ​ 1.notify()随机唤醒单个等待的线程

    ​ 2.notyfyAll() 唤醒所有等待的线程

    等待和唤醒的方法位于Object类中是因为等待和唤醒机制必须依赖于锁对象,使用锁对象调用等待和唤醒的方法,而锁对象属于任意对象,能够被任意对象调用的方法肯定位于Object类中

  • 由于等待和唤醒都需要使用锁对象调用,而只有在同步中才有锁对象,所以等待和唤醒机制必须在同步中完成

  • 如果一个线程处于等待状态,那么会立刻释放锁对象,此时cpu永远不会执行等待的线程,只有被其他线程唤醒之后并获取到锁对象才有可能被cpu执行

  • 入门代码:

    package com.itheima.sh.pro_consu_03;
    
    public class MyTask1 implements Runnable{
        @Override
        public void run() {
            //加同步
            synchronized ("abc"){//"abc"是同步锁t1
                System.out.println("A");
                System.out.println("B");
                try {
                    //让当前线程等待
                    //释放锁对象,cpu不会执行等待线程
                    "abc".wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                System.out.println("C");
                System.out.println("D");
    
            }
        }
    }
    
    package com.itheima.sh.pro_consu_03;
    
    public class MyTask2 implements Runnable{
        @Override
        public void run() {//t2
            //加同步
            synchronized ("abc"){//"abc"是同步锁
                System.out.println("1");
                System.out.println("2");
                //唤醒等待的线程
                "abc".notify();
                //因为t2占有锁
                System.out.println("3");
                System.out.println("4");
    
            }
        }
    }
    
    package com.itheima.sh.pro_consu_03;
    
    public class Test01 {
        public static void main(String[] args) throws InterruptedException {
    //        "abc".wait();
    //        "abc".notify();t1 等待 其他线程打断了等待的线程或者休眠的线程此时就会抛被打断异常 sleep(2min)
            //创建任务对象
            MyTask1 mt1 = new MyTask1();
            MyTask2 mt2 = new MyTask2();
    
            //创建线程
            Thread t1 = new Thread(mt1);
            Thread t2 = new Thread(mt2);
    
            //启动线程
            t1.start();
            //休眠
            Thread.sleep(5000);
            t2.start();
        }
    }
    
    

    图解:

在这里插入图片描述

  • 包子案例

    创建包子类:

    package com.itheima.sh.pro_con_04;
    //创建包子类:
    public class BaoZi {
        //成员变量
        String pi;//皮
        String xian;//馅
        //定义标记
        boolean flag = false;//false表示没有包子  true 表示有包子
    }
    
    

    创建生产包子的任务类:

    package com.itheima.sh.pro_con_04;
    
    /*
        创建生产包子的任务类:
        生产包子思想:
            在生产包子之前先判断是否有包子,如果有包子就不生产则等待;
            如果没有包子,则生产包子,唤醒吃货来吃包子。
     */
    public class ProduceBaoZi implements Runnable {
        //定义成员变量接收包子对象
        BaoZi baoZi;
    
        public ProduceBaoZi(BaoZi baoZi) {
            this.baoZi = baoZi;
        }
    
        @Override
        public void run() {
            //模拟无限生产使用死循环
            while (true) {
                //为了安全加同步
                synchronized ("abc") {//t1
                    // 在生产包子之前先判断是否有包子,如果有包子就不生产则等待;
                    //flag的值是true表示有包子
                    if (baoZi.flag) {
                        try {
                            //说明flag是true,表示有包子,不生产则等待;
                            "abc".wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    /*
                        如果能够执行到这里,说明没有执行"abc".wait();说明flag是false
                        没有包子,生产包子
    
                        如果没有包子,则生产包子,唤醒吃货来吃包子。
                     */
                    //给包子的成员变量赋值
                    baoZi.pi = "白面";
                    baoZi.xian = "韭菜大葱";
                    System.out.println("生产者已经生产完毕包子");
                    //上述代码已经生产完包子了,修改标记
                    baoZi.flag = true;
                    //唤醒吃货来吃包子。
                    "abc".notify();
    
                }
            }
        }
    }
    
    

    消费包子的任务类:

    package com.itheima.sh.pro_con_04;
    /*
        消费包子的任务类:
            消费者线程(吃货):吃包子之前先判断有没有包子,如果没有包子则等待;如果有包子,
            则吃包子(消费),吃完之后唤醒生产者线程生产包子
     */
    public class ChiHuo implements Runnable {//t2
        //定义成员变量接收包子对象
        BaoZi baoZi;
        public ChiHuo(BaoZi baoZi) {
            this.baoZi = baoZi;
        }
    
        @Override
        public void run() {
            //模拟无限吃包子使用死循环
            while (true){
                //为了安全加同步
                synchronized ("abc"){//t2
                    //吃包子之前先判断有没有包子,如果没有包子则等待;
                    //没有包子标记flag是false
                    if(!baoZi.flag){
                        try {
                            //如果没有包子则等待;
                            "abc".wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //如果能够执行到这里,说明没有执行 "abc".wait(); 说明flag是true
                    //表示有包子
                    //如果有包子,则吃包子(消费),吃完之后唤醒生产者线程生产包子
                    System.out.println("吃货吃了一个"+baoZi.pi+"皮,馅是"+baoZi.xian+"的包子");
                    //打印完毕之后包子被吃没了
                    baoZi.pi = null;
                    baoZi.xian = null;
                    //修改标记 包子吃没了,标记变为false
                    baoZi.flag = false;
                    //吃完之后唤醒生产者线程生产包子
                    "abc".notify();
                }
            }
        }
    }
    
    

    测试类

package com.itheima.sh.pro_con_04;
/*
    测试类
 */
public class Test01 {
    public static void main(String[] args) {
        //1.创建包子类对象
        BaoZi baoZi = new BaoZi();
        //2.创建生产者任务对象
        ProduceBaoZi pb = new ProduceBaoZi(baoZi);
        //3.创建生产者线程
        Thread t1 = new Thread(pb);
        //4.启动生产者线程
        t1.start();


        //5.创建消费者任务对象
        ChiHuo ch = new ChiHuo(baoZi);
        //6.创建消费者线程
        Thread t2 = new Thread(ch);
        //7.启动消费者线程
        t2.start();

    }
}

图解:

在这里插入图片描述

5.定时器Timer(了解)

  • 介绍: 定时器,可以设置线程在某个时间执行某件事情,或者某个时间开始,每间隔指定的时间反复的做某件 事情。定时器类是java.util.Timer类

  • 构造方法:

    Timer() 创建一个新计时器。
    
  • 方法:

    1.void schedule(TimerTask task, long delay) 安排在指定延迟后执行指定的任务。 
        	参数:
                  task:属于TimerTask类,由 Timer 安排为一次执行或重复执行的任务的抽象类。父接口是				    Runnable,该类实现了run方法,该方法也是抽象的:
                        abstract  void run() 此计时器任务要执行的操作。 
                        我们需要自定义类继承该抽象类并重写run方法并书写定时器需要执行的任务
        		  delay:表示过多少毫秒定时器开始执行任务task
        
    

    代码演示:

     /*
            1.void schedule(TimerTask task, long delay) 安排在指定延迟后执行指定的任务。
        	参数:
                  task:属于TimerTask类,由 Timer 安排为一次执行或重复执行的任务的抽象类。父接口是	Runnable,该类实现了run方法,该方法也是抽象的:
                        abstract  void run() 此计时器任务要执行的操作。
                        我们需要自定义类继承该抽象类并重写run方法并书写定时器需要执行的任务
        		  delay:表示过多少毫秒定时器开始执行任务task
         */
        private static void method_1() {
            //1.创建定时器类的对象Timer() 创建一个新计时器。
            Timer t = new Timer();
            //2.使用定时器类的对象调用方法执行任务
            t.schedule(new TimerTask(){
                @Override
                public void run() {
                    System.out.println("起床了");
                }
            },3000);
        }
    
     2.void schedule(TimerTask task, long delay, long period) 安排指定的任务从指定的延迟后开始进行														  重复的固定延迟执行。 
         	参数:
         		task:任务类
         		delay:过多场时间执行任务
         		period:每隔多场时间执行一次任务
    

    代码演示:

     /*
             void schedule(TimerTask task, long delay, long period) 安排指定的任务从指定的延迟后开始进行														  重复的固定延迟执行。
         	参数:
         		task:任务类
         		delay:过多场时间执行任务
         		period:每隔多场时间执行一次任务
         */
        private static void method_2() {
    
            //1.创建定时器类的对象Timer() 创建一个新计时器。
            Timer t = new Timer();
            //2.使用定时器类的对象调用方法执行任务
            t.schedule(new TimerTask(){
                @Override
                public void run() {
                    System.out.println("起床了");
                }
            },3000,1000);//3000表示3s之后自行任务  1000 表示以后每个1s执行一次
        }
    
    3.void schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务。 
        	参数:
        		task:定时器执行的任务
                time:指定的具体时间,时间一到就执行任务
    

    代码演示:

    /*
            void schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务。
        	参数:
        		task:定时器执行的任务
                time:指定的具体时间,时间一到就执行任务
         */
        private static void method_3() throws ParseException {
            //1.创建定时器类的对象Timer() 创建一个新计时器。
            Timer t = new Timer();
            //2.使用定时器类的对象调用方法执行任务
            //定义字符串
            String s = "2020-05-25 14:28:00";
            //创建日期格式化解析类对象
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            //将上述字符串解析为Date
            Date date = sdf.parse(s);
            t.schedule(new TimerTask() {
                @Override
                public void run() {
                    System.out.println("起床了");
                }
            },date);
        }
    
    4. void schedule(TimerTask task, Date firstTime, long period) 
              			安排指定的任务在指定的时间开始进行重复的固定延迟执行。 
         			参数:
         				task:任务对象
    					firstTime:指定日期,日期时间一到就执行任务
    					period:每隔一段时间执行一次任务
    

    代码演示:

    /*
             void schedule(TimerTask task, Date firstTime, long period)
              			安排指定的任务在指定的时间开始进行重复的固定延迟执行。
         			参数:
         				task:任务对象
    					firstTime:指定日期,日期时间一到就执行任务
    					period:每隔一段时间执行一次任务
         */
        private static void method_4() throws ParseException {
            //1.创建定时器类的对象Timer() 创建一个新计时器。
            Timer t = new Timer();
            //2.使用定时器类的对象调用方法执行任务
            //定义字符串
            String s = "2020-05-25 14:31:10";
            //创建日期格式化解析类对象
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            //将上述字符串解析为Date
            Date date = sdf.parse(s);
            t.schedule(new TimerTask() {
                @Override
                public void run() {
                    System.out.println("起床了");
                }
            },date,1000);//1000表示每隔1s执行一次
        }
    

6.Lambda(掌握)

lambda介绍

  • 属于jdk8以后的新特性,为了简化代码开发的。代码写的变少了,但是完成的功能是一样的。write less do more

  • lambda引入

    package com.itheima.sh.lambda_06;
    
    public class Test01 {
        public static void main(String[] args) {
            //使用匿名内部类方式完成多线程
            /*Runnable r = new Runnable() {
                @Override
                public void run() {
                    System.out.println("黑马程序员");
                }
            };
            //启动线程
            new Thread(r).start();*/
    
            //启动线程
           /* new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("黑马程序员");
                }
            }).start();*/
           //使用lambda简化匿名内部类方式
            new Thread(()->{ System.out.println("黑马程序员");}).start();
        }
    }
    
    

Lambda的格式

lambda由三部分组成:

  • (参数列表):一些参数,和之前的方法参数是一样的

  • ->: 一个箭头,属于全新的语法,表示将小括号中的数据执向给后面的大括号

  • {}:一段代码,和我们之前学习的方法体一样

    lambda由三个一组成。完成接口中的抽象方法。

    举例:

    Runnable接口中的抽象方法:run:
    public abstract void run();
    没有方法体,我们可以使用lambda完成方法
    

    lambad代码演示:

    package com.itheima.sh.lambda_06;
    
    import java.util.Arrays;
    import java.util.Comparator;
    
    public class Test02 {
        public static void main(String[] args) {
            //需求:使用Arrays数组工具类中的sort方法对自定义类型的数组的对象年龄进行降序排序
            //1.创建数组存储Person对象
            Person[] arr = {new Person("柳岩", 19), new Person("冰冰", 17), new Person("杨幂", 20)};
            //2.使用数组工具类Arrays调用sort方法对上述Person对象的年龄降序排序
            //static <T> void sort(T[] a, Comparator<? super T> c)根据指定比较器产生的顺序对指定对象数组进行排序。
            //int compare(T o1, T o2); o1- o2 升序   o2  -  o1  降序
            //匿名内部类
           /* Arrays.sort(arr, new Comparator<Person>() {
                @Override
                public int compare(Person o1, Person o2) {
                    return o2.age - o1.age;
                }
            });*/
           //使用lambda完成
            //sort方法第二个参数是一个Comparator接口,并且该接口中有一个抽象方法,此时我们可以使用lambda完成该抽象方法
            //lambada:三个一:1)一些参数 2)一个箭头  3)一段代码
            //int compare(T o1, T o2);
            Arrays.sort(arr,(Person o1,Person o2)->{return o2.age - o1.age;});
            //输出
            System.out.println(Arrays.toString(arr));
        }
    }
    
    

    小结:

    1.lambda组成:一些参数 一个箭头 一段代码

    2.使用lambda可以完成接口中的抽象方法

省略格式

格式:

(参数类型 参数名,参数类型 参数名,...)->{一段代码}

1.参数:

​ 1)参数类型可以省略,无论多少个参数,类型都可以不写

​ 2) 如果只有一个参数,小括号也可以省略

2.方法体中的内容,如果只有一行代码,无论是否有返回值,那么可以一起省略 : return 大括号 大括号中的分 号。方法体内容要省略都省略,要么都不能省略

package com.itheima.sh.lambda_06;

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

public class Test02 {
    public static void main(String[] args) {
        //需求:使用Arrays数组工具类中的sort方法对自定义类型的数组的对象年龄进行降序排序
        //1.创建数组存储Person对象
        Person[] arr = {new Person("柳岩", 19), new Person("冰冰", 17), new Person("杨幂", 20)};
        //2.使用数组工具类Arrays调用sort方法对上述Person对象的年龄降序排序
        //static <T> void sort(T[] a, Comparator<? super T> c)根据指定比较器产生的顺序对指定对象数组进行排序。
        //int compare(T o1, T o2); o1- o2 升序   o2  -  o1  降序
        //匿名内部类
       /* Arrays.sort(arr, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o2.age - o1.age;
            }
        });*/
       //使用lambda完成
        //sort方法第二个参数是一个Comparator接口,并且该接口中有一个抽象方法,此时我们可以使用lambda完成该抽象方法
        //lambada:三个一:1)一些参数 2)一个箭头  3)一段代码
        //int compare(T o1, T o2);
//        Arrays.sort(arr,(Person o1,Person o2)->{return o2.age - o1.age;});
        Arrays.sort(arr,(o1,o2)->o2.age - o1.age);
        //输出
        System.out.println(Arrays.toString(arr));
    }
}

Lambda的前提条件

1.必须有函数式接口:只有一个抽象方法的接口,可以有默认方法,静态方法。其实我们使用lambda完成接口中的抽象方法

2.要求某个方法的形参位置的类型必须是函数式接口类型

举例:

//满足条件1
public interface Runnable {
    public abstract void run();
}
//满足条件2 Thread类的构造方法的形参位置是Runnable接口类型
public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

最后实现:
     new Thread(()->{ System.out.println("黑马程序员");}).start();
	 Runnable target = ()->{ System.out.println("黑马程序员");};

补充:

匿名内部类和lambda区别:

​ 1.匿名内部类可以是类,也可以是接口

​ lambda只能是一个接口

​ 2.匿名内部类对父类或者父接口中的抽象方法个数没有要求

​ lambda要父接口中的抽象方法只能有一个

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java 中,可以通过实现 Callable 接口,实现多线程操作。Callable 接口与 Runnable 接口类似,都是用来实现多线程操作的接口。但是,Callable 接口支持返回结果和抛出异常。 下面是一个简单的示例,演示如何使用 Callable 实现多线程操作: ``` import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class MyCallable implements Callable<String> { private String name; public MyCallable(String name) { this.name = name; } @Override public String call() throws Exception { System.out.println("Thread " + name + " is running..."); Thread.sleep(5000); return "Hello from thread " + name; } public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newFixedThreadPool(3); Future<String> result1 = executorService.submit(new MyCallable("Thread-1")); Future<String> result2 = executorService.submit(new MyCallable("Thread-2")); Future<String> result3 = executorService.submit(new MyCallable("Thread-3")); System.out.println(result1.get()); System.out.println(result2.get()); System.out.println(result3.get()); executorService.shutdown(); } } ``` 在上面的示例中,我们创建了一个实现了 Callable 接口的类 MyCallable。在 call() 方法中,我们输出了当前线程的名称,然后让线程休眠 5 秒,最后返回一个字符串。 在 main() 方法中,我们创建了一个 ExecutorService,并向其提交了三个 MyCallable 对象。ExecutorService.submit() 方法返回一个 Future 对象,可以使用 get() 方法获取 MyCallable 对象的返回值。最后,我们关闭了 ExecutorService。 执行上面的代码,可以看到如下输出: ``` Thread Thread-1 is running... Thread Thread-2 is running... Thread Thread-3 is running... Hello from thread Thread-1 Hello from thread Thread-2 Hello from thread Thread-3 ``` 从输出结果可以看出,我们创建的三个线程都在运行,而且每个线程都在休眠 5 秒钟。而且,我们使用 Future 对象获取了每个线程的返回值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娃娃 哈哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值