03-Java核心类库_多线程

目录

四,多线程

1,线程与进程

1.1 线程与进程

1.2 线程调度

2,同步与异步&并发与并行

2.1 同步与异步

2.2 并发与并行

3,继承Thread

3.1 程序实例

3.2 时序图

3.3 补充

4,实现Runnable

4.1 使用方法

4.2 实现Runnable与继承Thread

5,Thread类

5.1 常用构造方法

5.2 常用其他方法

6,设置和获取线程名称

7,线程休眠sleep

8,线程的中断

9,守护线程

9.1 概述

9.2 实例

10,线程安全1-同步代码块

10.1 线程不安全的原因

10.2 代码实例

11,线程安全2-同步方法

12,线程安全3-显式锁Lock

13,公平锁与非公平锁

13.1 区别

13.2 实现方法

14,多线程通信问题

15,生产者与消费者

15.1 前提条件

15.2 问题一:饭菜的属性错乱

15.3 问题二:一次性消费/生产多个菜品

15.4 解决方法

16,线程的六种状态

17,带返回值的线程Callable

17.1 Runnable 与 Callable

 17.2 Callable使用步骤

17.3 常用方法

18,线程池概述

18.1 为什么需要线程池

18.2 缓存线程池

18.3 定长线程池

18.4 单线程线程池

18.5 周期定长线程池

19,Lambda表达式

19.1 为什么要用lambda表达式

19.2 使用实例


四,多线程

1,线程与进程

1.1 线程与进程

进程:

  • 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间

线程:

  • 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程
  • 线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程

1.2 线程调度

目的是为了更合理的利用CPU

分时调度

  • 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。

抢占式调度

  • 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
  • CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使用率更高。

 

2,同步与异步&并发与并行

2.1 同步与异步

同步:排队执行 , 效率低但是安全.

异步:同时执行 , 效率高但是数据不安全

2.2 并发与并行

并发:指两个或多个事件在同一个时间段内发生。

并行:指两个或多个事件在同一时刻发生(同时发生)。

3,继承Thread

3.1 程序实例

3.2 时序图

3.3 补充

每个线程都有自己的栈空间,共用一份堆内存

4,实现Runnable

4.1 使用方法

另一种实现多线程的方法

  • 创建自定义类实现Runnable接口,并重写run方法;
  • 用自定义类创建一个对象r;
  • 用Thread类创建一个对象t,并将r作为t构造方法的参数;

4.2 实现Runnable与继承Thread

1)实现Runnable与继承Thread相比有如下优势

  • 1,通过创建任务,然后给线程分配任务的方式实现多线程,更适合多个线程同时执行任务的情况;
  • 2,可以避免单继承所带来的局限性(Java允许实现多个接口,但不允许继承多个父类);
  • 3,任务与线程是分离的,提高了程序的健壮性;
  • 4,后期学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程;

 2)Thread也有一定的好处

5,Thread类

5.1 常用构造方法

5.2 常用其他方法

停止线程的方法:声明一个变量,线程不断监控这个变量,一旦变量达到某种条件调用return即可

所有的用户线程结束,程序才能结束。守护线程是为了守护用户线程,用户线程可以自动结束,所有用户线程结束后,守护线程便会像没有一样。

6,设置和获取线程名称

7,线程休眠sleep

8,线程的中断

过时的stop方法可以直接中断线程,但是如果线程来不及释放资源,会造成一部分垃圾无法回收;

这里采用添加中断标记的方法:调用interrupt方法,子线程执行时捕获中断异常,并在catch块中,添加处理释放资源的代码;

9,守护线程

9.1 概述

线程分为守护线程和用户线程;

  • 用户线程:当一个进程不包含任何存活的用户线程时,进程结束;
  • 守护线程:守护用户线程,当最后一个用户线程结束后,所有守护线程自动死亡;

直接创建的都是用户线程;

设置守护线程:线程对象.setDaemon(true);

9.2 实例

1)不设置守护线程

2)设置为守护线程

10,线程安全1-同步代码块

10.1 线程不安全的原因

多个线程争抢同一个数据,使得数据在判断使用时出现不一致的情况。解决方法,保证一段数据同时只能被一个线程使用(排队使用)。

解决方案一:同步代码块

格式:

synchronize(锁对象){


}

10.2 代码实例

1,不加锁

2,加锁后

package com.kaikeba;

import javax.xml.namespace.QName;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.Scanner;
import java.util.SimpleTimeZone;

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        //线程不安全
        //解决方案1  同步代码块
        //格式:synchronized(锁对象){
        //
        //
        //      }
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }
    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        private Object o = new Object();
        @Override
        public void run() {
            //Object o = new Object();    //这里不是同一把锁,所以锁不住
            while (true) {
                synchronized (o) {
                    if (count > 0) {
                        //卖票
                        System.out.println("正在准备卖票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                    }else {
                        break;
                    }

                }
            }
        }
    }
}

11,线程安全2-同步方法

同步代码块粒度较细,可以给一行代码单独加锁,同步方法顾名思义,是给方法加锁;

package com.kaikeba;

import javax.xml.namespace.QName;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.Scanner;
import java.util.SimpleTimeZone;

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        //线程不安全
        //解决方案2  同步方法
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }
    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        @Override
        public void run() {

            while (true) {
                boolean flag = sale();
                if(!flag){
                    break;
                }
            }
        }
        public synchronized boolean sale(){
            if (count > 0) {
                //卖票
                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                return true;
            }
            return false;

        }
    }
}

给方法上锁,对应的锁对象就是this, 如果是静态修饰方法的话,锁对象为类名.class(比如这里sale方法若被修饰为静态方法的话,锁对象为Ticket.class,也就是字节码文件对象)

针对以上代码来说 ,锁对象如下:

12,线程安全3-显式锁Lock

同步方法和同步代码块都属于隐式锁,显式锁则是程序员手动加锁、解锁;

package thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


//同步代码块和同步方法都属于隐式锁
//线程同步lock

public class Demo10 {
    public static void main(String[] args) {
        Object o = new Object();
        //线程不安全
        //解决方案1   显示锁  Lock  子类 ReentrantLock

        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //总票数
        private int count = 10;
        //参数为true表示公平锁    默认是false 不是公平锁
        private Lock l = new ReentrantLock(true);
        @Override
        public void run() {
            while (true) {
                l.lock();
                    if (count > 0) {
                        //卖票
                        System.out.println("正在准备卖票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
                    }else {
                        break;
                    }
                    l.unlock();
            }
        }
    }
}

13,公平锁与非公平锁

13.1 区别

公平锁:先来先得,遵循排队;

非公平锁:大家一起抢(同步代码块,同步方法,显式锁都属于非公平锁);

13.2 实现方法

在显式锁实例化时,传入参数true()

14,多线程通信问题

主要借助于wait和notify函数实现

15,生产者与消费者

15.1 前提条件

厨师cook为生产者线程,服务员waiter为消费者线程,食物为生产与消费的物品;

假设目前只有一个厨师,一个服务员,一个盘子。理想状态是:厨师生产一份饭菜,服务员端走一份,且饭菜的属性未发生错乱;

厨师可以制作两种口味的饭菜,制作100次;

服务员可以端走饭菜100次;

15.2 问题一:饭菜的属性错乱

1)实验代码

package com.kaikeba;

public class Demo1 {
    public static void main(String[] args) {
        //多线程通信    生产者与消费者问题
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }
    //厨师
    static class Cook extends Thread{
        private Food f;
        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if(i%2==0){// 设计两种菜色
                    f.setNameAndTaste("老干妈小米粥","香辣味");
                }else {
                    f.setNameAndTaste("煎饼果子","甜辣味");
                }
            }
        }
    }
    //服务员
    static class Waiter extends Thread{
        private Food f;
        public Waiter(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;
        private String taste;
        public void setNameAndTaste(String name,String taste){// 生产
            this.name = name;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.taste = taste;

        }
        public void get(){  // 消费
            System.out.println("服务员端走的菜的名称是:"+name+",味道是:"+taste);
        }
    }
}

2)错误现象 

3)错误原因

15.3 问题二:一次性消费/生产多个菜品

1)实验代码

为了防止在生产过程中setNameAndTaste出现时间片切换,可以用synchronized修饰此方法;

public synchronized void setNameAndTaste(String name,String taste){// 生产
            this.name = name;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.taste = taste;

        }
        public synchronized void get(){  // 消费
            System.out.println("服务员端走的菜的名称是:"+name+",味道是:"+taste);
        }

2)运行效果

3)原因分析

synchronized只是确保了方法内部不会发生线程切换,但并不能保证生产一个消费一个的逻辑关系;

15.4 解决方法

厨师做完饭后喊醒服务员,自己睡着。服务员送完饭后喊醒厨师,自己睡着;

主要修改的部分为setNameAndTaste与get方法:

public synchronized void setNameAndTaste(String name,String taste){// 生产
            if(flag) {
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;        // 表示饭菜生产完毕
                this.notifyAll();    // 叫醒服务员
                try {
                    this.wait();     // 睡着
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public synchronized void get(){  // 消费
            if(!flag){
                System.out.println("服务员端走的菜的名称是:"+name+",味道是:"+taste);
                flag = true;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }

可以看出饭菜是交替产生并消费的; 

 

16,线程的六种状态

17,带返回值的线程Callable

新的创建线程的方式。之前的创建线程方式:实现Thread的子类、实现Runnable接口,可以看成是和主线程并发执行的;

这里要讲的线程更像是主线程指派的一个任务,主线程可以获得其返回值;

17.1 Runnable 与 Callable

1)接口定义

接口定义 

//Callable接口 
public interface Callable<V> { 
    V call() throws Exception; 
}

//Runnable接口 
public interface Runnable { 
    public abstract void run(); 
}

2)相同点

都是接口

都可以编写多线程程序

都采用Thread.start()启动线程

3)不同点

Runnable没有返回值;Callable可以返回执行结果

Callable接口的call()允许抛出异常;Runnable的run()不能抛出

 17.2 Callable使用步骤

1. 编写类实现Callable接口 , 实现call方法 

class XXX implements Callable<T> { 
    @Override 
    public <T> call() throws Exception { 
        return T; 
    } 
} 

2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象 

FutureTask<Integer> future = new FutureTask<>(callable); 

3. 通过Thread,启动线程 

new Thread(future).start();

17.3 常用方法

1)方法介绍 

 2)代码示例

package com.kaikeba;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class Demo1 {
    public static void main(String[] args) {
        Callable<Integer> c = new MyCallable();
        FutureTask<Integer> f = new FutureTask<>(c);
        new Thread(f).start();
        for(int i = 0; i < 10; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
    static class MyCallable implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
//            Thread.sleep(100);// 睡眠过后 给出结果
            for(int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + i);
            }
            return 100;
        }
    }

}

未使用get方法时,主线程和另一个线程交替执行

使用get方法

 

18,线程池概述

18.1 为什么需要线程池

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间.

线程池就是一个容纳多个线程的容 器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。

Java中的四种线程池(对象均为ExecutorService) 

18.2 缓存线程池

1)概述

/*** 缓存线程池. 
* (长度无限制) 
* 执行流程: 
* 1. 判断线程池是否存在空闲线程 
* 2. 存在则使用 
* 3. 不存在,则创建线程 并放入线程池, 然后使用 
*/ 
ExecutorService service = Executors.newCachedThreadPool(); 
//向线程池中 加入 新的任务 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("线程的名称:"+Thread.currentThread().getName()); 
    } 
}); 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("线程的名称:"+Thread.currentThread().getName()); 
    } 
}); 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("线程的名称:"+Thread.currentThread().getName()); 
    } 
});

2)代码示例

package com.kaikeba;

import java.util.concurrent.*;

public class Demo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*** 缓存线程池.
         * (长度无限制)
         * 执行流程:
         * 1. 判断线程池是否存在空闲线程
         * 2. 存在则使用
         * 3. 不存在,则创建线程 并放入线程池, 然后使用
         */
        ExecutorService service = Executors.newCachedThreadPool();
        // 指挥线程池执行新的任务
        service.execute(new Runnable() {// 匿名内部类
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "滴滴滴");
            }
        });
        service.execute(new Runnable() {// 匿名内部类
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "滴滴滴");
            }
        });
        service.execute(new Runnable() {// 匿名内部类
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "滴滴滴");
            }
        });

    }
}

睡眠一段时间后,添加新的任务,查看是否能利用线程池中已存在的线程:

18.3 定长线程池

1)概述

/**
* 定长线程池. 
* (长度是指定的数值) 
* 执行流程:
* 1. 判断线程池是否存在空闲线程 
* 2. 存在则使用 
* 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 
* 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程 
*/ 
ExecutorService service = Executors.newFixedThreadPool(2); 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("线程的名称:"+Thread.currentThread().getName()); 
    } 
}); 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("线程的名称:"+Thread.currentThread().getName()); 
    } 
});

2)代码示例

设定线程池大小为2,即线程池中最多只允许存在两个线程;

前两个线程执行时,均sleep三秒钟;

第三个任务由于线程池已满,不能开辟新的线程,所以必须等线程池中有空闲线程出现才可以执行;

package com.kaikeba;

import java.util.concurrent.*;

public class Demo1 {
    /*定长线程池
    长度是指定的线程池
    加入任务后的执行流程
        1 判断线程池是否存在空闲线程
        2 存在则使用
        3 不存在空闲线程  且线程池未满的情况下  则创建线程  并放入线程池中  然后使用
        4 不存在空闲线程  且线程池已满的情况下  则等待线程池的空闲线程
    **/
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
    }
}

 

18.4 单线程线程池

1)概述

效果与定长线程池 创建时传入数值1 效果一致. 
/**
* 单线程线程池. 
* 执行流程: 
* 1. 判断线程池 的那个线程 是否空闲 
* 2. 空闲则使用 
* 3. 不空闲,则等待 池中的单个线程空闲后 使用 
*/ 
ExecutorService service = Executors.newSingleThreadExecutor(); 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("线程的名称:"+Thread.currentThread().getName()); 
    } 
}); 
service.execute(new Runnable() { 
    @Override 
    public void run() { 
        System.out.println("线程的名称:"+Thread.currentThread().getName()); 
    } 
});

2)代码示例

package com.kaikeba;

import java.util.concurrent.*;

public class Demo1 {
    /*单线程线程池
    执行流程
        1 判断线程池的那个线程是否空闲
        2 空闲则使用
        3 不空闲则等待它空闲后再使用
    **/
    public static void main(String[] args) {
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        });
    }
}

 

18.5 周期定长线程池

1)概述

public static void main(String[] args) { 
    /**
    * 周期任务 定长线程池. 
    * 执行流程: 
    * 1. 判断线程池是否存在空闲线程 
    * 2. 存在则使用 
    * 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 
    * 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程 
    *
    * 周期性任务执行时: 
    * 定时执行, 当某个时机触发时, 自动执行某任务 .
    */ 
    ScheduledExecutorService service = Executors.newScheduledThreadPool(2); 

    /**
    * 定时执行 
    * 参数1. runnable类型的任务 
    * 参数2. 时长数字 
    * 参数3. 时长数字的单位 
    */ 

    /*
    service.schedule(new Runnable() { 
        @Override 
        public void run() { 
            System.out.println("俩人相视一笑~ 嘿嘿嘿"); 
        } 
    },5,TimeUnit.SECONDS); 
    */

    /**
    * 周期执行 
    * 参数1. runnable类型的任务 
    * 参数2. 时长数字(延迟执行的时长) 
    * 参数3. 周期时长(每次执行的间隔时间) 
    * 参数4. 时长数字的单位 
    */ 
    service.scheduleAtFixedRate(new Runnable() { 
        @Override 
        public void run() { 
            System.out.println("俩人相视一笑~ 嘿嘿嘿"); 
        } 
    },5,2,TimeUnit.SECONDS); 
}

2)代码示例

package com.kaikeba;

import java.util.concurrent.*;

public class Demo1 {
    /*周期任务  定长线程池
    执行流程
        1 判断线程池是否存在空闲线程
        2 存在则使用
        3 不存在空闲线程  且线程池未满的情况下  则创建线程  并放入线程池中  然后使用
        4 不存在空闲线程  且线程池已满的情况下  则等待线程池的空闲线程

        周期性任务执行时
                定时执行 当某个任务触发时  自动执行某任务
    **/
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
        //定时执行一次
        //参数1:定时执行的任务
        //参数2:时长数字
        //参数3:2的时间单位    Timeunit的常量指定
       /* scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        },5, TimeUnit.SECONDS);      //5秒钟后执行*/

        /*
        周期性执行任务
            参数1:任务
            参数2:延迟时长数字(第一次执行延迟的时间)
            参数3:周期时长数字(每隔多久执行一次)
            参数4:时长数字的单位
        * **/
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"锄禾日当午");
            }
        },5,1,TimeUnit.SECONDS);
    }
}

19,Lambda表达式

19.1 为什么要用lambda表达式

对于某些应用场景,我们更注重于结果,如果能用一个方法解决,那么通过创建对象、调用方法的方式可能会更加繁琐;

1)冗余的Runnable方法

public class Demo1 {
    /**
     * lambda表达式
     * 函数式编程思想(注重结果,而面向对象则是通过创建对象,解决问题)
     * @param args
     */
    public static void main(String[] args) {
        // 冗余的Runnable代码
        Runnable r = new MyRunnable();
        Thread t = new Thread(r);
        t.start();  // 写了这么多 只为了完成一个简单的任务
    }
    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            System.out.println("任务完成!");
        }
    }
}

2)通过匿名内部类简化代码

public class Demo1 {
    /**
     * lambda表达式
     * 函数式编程思想(注重结果,而面向对象则是通过创建对象,解决问题)
     * @param args
     */
    public static void main(String[] args) {
        // 冗余的Runnable代码
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("任务完成!");
            }
        });
        t.start();  // 写了这么多 只为了完成一个简单的任务
    }
}

19.2 使用实例

1)不使用lambda

package com.kaikeba;

import java.util.concurrent.*;

public class Demo1 {
    /**
     * lambda表达式
     * 函数式编程思想(注重结果,而面向对象则是通过创建对象,解决问题)
     * @param args
     */
    public static void main(String[] args) {
        print(new MyMath() {
            @Override
            public int sum(int x, int y) {
                return x + y;
            }
        }, 100, 200);
    }
    public static void print(MyMath m, int x, int y){
        int num = m.sum(x, y);
        System.out.println(num);
    }
    static  interface MyMath{
        int sum(int x, int y);
    }
}

2)使用lambda

不需要实现接口、实例化对象;

package com.kaikeba;

import java.util.concurrent.*;

public class Demo1 {
    /**
     * lambda表达式
     * 函数式编程思想(注重结果,而面向对象则是通过创建对象,解决问题)
     * @param args
     */
    public static void main(String[] args) {
        print((int x, int y) -> {
                return x + y;
            }, 100, 200);
    }
    public static void print(MyMath m, int x, int y){
        int num = m.sum(x, y);
        System.out.println(num);
    }
    static  interface MyMath{
        int sum(int x, int y);
    }
}

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值