Java多线程技术概述(知识点整理)

时间:2021-2-20 作者:admin

文章目录

多线程技术概述

线程和进程

进程:

  • 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。并且进程互不共享内存空间,除非通过特殊手段。程序就是进程,例如:电脑的各个图标

线程:

  • 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程。
  • 线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程。
  • 用到哪一个哪一个被唤醒,例如:和多人聊天 sleep 生产者和消费者

线程和进程的关系:一个进程由多个线程支撑运行,

线程调度

分时调度

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

抢占式调度

  • 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调用。

  • CPU使用抢占式调度模式在多个线程间进行着高速的切换,对于CPU的一个核心而言,某个时刻,只能执行一个线程,而CPU在多个线程间切换的速度相对于我们的感觉要快,看上去就是在同一时刻运行。其实,多线程程序并不能提高程序的运行速度,但能提高程序的运行效率,让CPU的使用率更高。

同步与异步

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

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

并发与并行

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

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

两种创建方式

Thread
public static void main(String[] args){
    MyThread m = new MyThread();
    m.start();
    for(int i=0;i<10;i++){
        System.out.println("汗滴禾下土"+i);
    }
}
class MyThread extends Thread{
    //run方法就是线程要执行的任务方法
    @Override
    public void run(){
        //这里的代码 就是一条新的执行路径
        //这个执行路径的触发方式,不是调用run方法,而是通过thread对象的start来启动任务
        for(int i=0;i<10;i++){
            System.out.println("锄禾日当午"+i);
        }
    }
}
//结果主程序和线程的输出语句同时执行。

执行流程图如下所示:

Java多线程技术概述(知识点整理)

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

Runnable
public static void main(String[] args){
    //实现Runnable
    //1.创建一个任务对象
    MyRunnable r = new MyRunnable();
    //2.创建一个线程,并为其分配一个任务
    Thread t = new Thread(r);
    //3.执行这个线程
    t.start();
    for(int i=0;i<10;i++){
        System.out.println("疑是地上霜"+i);
    }
}
//这是用于给线程执行的任务
class MyRunnable implements Runnable{
    @Override
    public void run(){
        //线程的任务
        for(int i=0;i<10;i++){
            System.out.println("床前明月光"+i);
        }
    }
}

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

  1. 通过创建任务,然后给线程分配的方式来实现的多线程。更适合多个线程同时执行相同任务的情况;
  2. 可以避免单继承所带来的局限性;
  3. 任务与线程本身是分离的,提高了程序的健壮性;
  4. 线程池技术,只接受Runnable类型的任务,不接受Thread类型的线程;
public static void main(String[] args){
    new Thread(){//通过匿名内部类的方式创建一个线程
        @Override
        public void run(){
            for(int i=0;i<10;i++){
                System.out.println("一二三四五")
            }
        }
    }.start();
    for(int i=0;i<10;i++){
        System.out.println("六七八九十"+i);
    }
}

线程常用方法

getName()与setName()

获取线程名称与设置线程名称:

public static void main(String[] args){
    System.out.println(Thread.currentThread().getName());//输出:main
    new Thread(new MyRunnable()).start();//输出:Thread-0
    new Thread(new MyRunnable()).start();//输出:Thread-1
    new Thread(new MyRunnable()).start();//输出:Thread-2
    new Thread(new MyRunnable(),"锄禾日当午").start;//输出:锄禾日当午     注意,这个方法是给线程命名
    Thread t = new Thread(new MyRunnable());//另一种命名的方法
    t.setName("锄禾日当午");
    t.start();
}
static class MyRunnable implements Runnable{
    @Override
    public void run(){
        Sysmte.out.println(Thread.currentThread().getName());
    }
}
sleep()

线程的休眠:

public static void main(String[] args){
    for(int i=0;i<10;i++){
        System.out.println(i);
        Thread.sleep(1000);//每隔1秒输出一个数
    }
}

线程阻塞

线程阻塞不光指的是线程休眠,线程是一条执行路径,比如说:线程在执行代码时,它的执行路径有100行,从第一行到第一百行是它的整体执行路径,这100行中某10行可能是为了读取某个文件。这文件读取可能耗时1秒钟,那么这1秒钟也是阻塞的,停在那读文件,后面读完才会执行。可以把阻塞理解成所有消耗时间的操作。就像上面的读取文件,它会使线程等待在那个位置,直到读取完毕,不会往下执行,除非文件读完。就像控制台等待用户输入,用户不输入,程序就不会继续往下执行。这就是线程阻塞,我们也称其为耗时操作。

线程中断

一个线程是一个独立的执行路径,它是否应该结束,应该由其自身决定。线程启动过程中可能会使用N多资源,也涉及到N多需要释放的资源,那么这时如果由外部直接把线程掐死,那么极有可能导致线程占用的资源来不及释放而一直占用,从而产生内存垃圾,这个垃圾是你不能回收的垃圾。也有可能占用一些硬件资源,导致硬件资源来不及释放,其他软件没有办法再去使用。

线程中断不用stop方法,因为此方法是直接把线程掐死。我们应该使用interrupt方法,通过该方法给线程打上一个标记,在特殊情况下线程会检查iterrupt的状态,如果检查到就是报异常,就是告诉线程该自杀了,线程在捕获异常语句块里调用return方法就会中断线程。

public static void main(String[] args){
    Thread t1 = new Thread(new MyRunnable());
    t1.start();
    for(int i=1;i<=5;i++){
        System.out.println(Thread.currentThread().getName()+":"+i);
        try{
            Thread.sleep(1000);
        } catch(InterruptedException e) {
        	e.printStackTrace();
        }
    }
    //给线程t1添加中断标记
    t1.interrupt();
}
static class MyRunnable implements Runnable{
    @Override
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            try{
                Thread.sleep(1000);
            } catch(InterruptedException e) {
                System.out.println("发现了中断标记,我们这个线程自杀");
                return;
            }
        }
    }
}

Java多线程技术概述(知识点整理)

守护线程

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

用户线程:当一个进程不包含任何存活的用户线程时,进程结束;

守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。

public static void main(String[] args){
    Thread t1 = new Thread(new MyRunnable());
    t1.setDaemon(true);//设置t1为守护线程
    t1.start();
    for(int i=1;i<=5;i++){
        System.out.println(Thread.currentThread().getName()+":"+i);
        try{
            Thread.sleep(1000);
        } catch(InterruptedException e) {
        	e.printStackTrace();
        }
    }
}
static class MyRunnable implements Runnable{
    @Override
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            try{
                Thread.sleep(1000);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Java多线程技术概述(知识点整理)

线程安全问题

public static void main(String[] args){
    //线程不安全
    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(){
        wile(count>0){
            System.out.println("正在准备卖票");
            try{
                Thread.sleep(1000);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
            count--;
            System.out.println("出票成功,余票:"+count);
        }
    }
}//三个线程会有可能会同时进入循环,同时调用count,从而造成count<0的情况发生

Java多线程技术概述(知识点整理)

synchronized(线程同步)

线程同步有三种方式,三种加锁的方式。

1、同步代码块

格式:synchronized(锁对象){ }

Java中任何对象都可以作为锁对象存在。多个线程应该抢一把锁,而不是一个线程一把锁。这样就不会出现同时操作造成数据混乱的情况了,因为都是排队执行代码块里的代码。

public static void main(String[] args){
    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;
    Object o = new Object();
    @Override
    public void run(){
        //Object o = new Object();//注意:如果锁声明在这里,那三个线程调用run方法都会重新定义一把锁,那就还是同时执行,不是排队执行了。
        while(true){
            synchronized(o){//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;
            	}
            }
        }
    }
}

Java多线程技术概述(知识点整理)

2、同步方法

如果不是静态的(static)的同步方法,它的锁对象为this;如果是静态的同步方法,它的锁对象为类名.class,比如下方的例子中,如果是静态的,则锁对象为Ticket.class

public static void main(String[] args){
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    	//new Thread(new Ticket()).start()//这里不是同一把锁,所以是同时执行(异步),不是排队执行(同步)了。
}
static class Ticket implements Runnable{
        private int count = 10;
        @Override
        public void run(){
            //synchronized(){  }//如果这里同步代码块和同步方法同时用了一把锁,那么一个线程执行同步方法活同步代码块时,其他线程对同步方法和同步代码块都不能执行。就好比商店里的试衣间,大门和试衣间的门用的是同一把锁,那么只要有一个人(线程)在使用其中的一个试衣间,那么其他人连大门都进不去,只能等待里面的人用完试衣间把锁打开。
            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;
            } else {
                return false;
            }
        }
}

Java多线程技术概述(知识点整理)

3、显示锁(Lock)

同步代码块和同步方法都属于隐式锁。

显示锁Lock子类ReentrantLock

public static void main(String[] args){
    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 Lock l = new ReentrantLock();//显示锁
    @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();
        }
    }
}

Java多线程技术概述(知识点整理)

公平锁和非公平锁

公平锁就是排队时谁先来,谁就先用这个锁。

非公平锁就是排队时抢着来,谁先抢到谁就用,上述Java线程同步的三种方式就是非公平锁。

在显示锁中定义时,构造函数的第一个参数设为true就是公平锁,false就是不公平锁,例如:

private Lock l = new ReentrantLock(true)//fair参数设为true,就表示公平锁

线程死锁

两个线程相互等待对方释放锁就造成了死锁。比如某商城有A、B两个试衣间,客户甲进入了A试衣间,客户乙进入了B试衣间,但是客户甲觉得A试衣间不好用,想换到B试衣间,所以客户甲在A试衣间中等客户乙用完B试衣间出来。巧的是客户已也觉得B试衣间不好用,也想换到A试衣间,所以客户乙也再等客户甲用完A试衣间出来。双方都不知道对方都在等自己用完试衣间出来,而造成的一种相互等待的状态,就叫做死锁。

public static void main(String[] args){
    Culprit c = new Culprit();
    Police p = new Police();
    new MyThread(c,p).start();
    c.say(p);
    //在这里主线程和MyThread线程同时执行,主线程拿到了罪犯类里的同步方法锁,只有主线程能调用罪犯类里的同步方法。而MyThread线程拿到了警察类里的同步方法的锁,只有MyThread线程能调用警察类里的同步方法。这样,主线程就需要等待MyThread线程释放所占用的锁,而MyThread线程也同样需要等待主线程释放所占用的锁,这样就造成了两线程相互等待的状态,从而形成了死锁。
    //如果想要两个线程不造成死锁,那么就需要在其中一个线程还没有抢占到锁时,程序就执行完毕。这样才不会造成死锁。
    //避免死锁的方法就是,你已经调用了一个同步方法,就不要再继续调用其他同步方法了,避免造成死锁。
}
static class MyThread extends Thread{
    private Culprit c;
    private Police p;
    public MyThread(Culprit c,Police p){
        this.c = c;
        this.p = p;
    }
    @Override
    public void run(){
        p.say(c);
    }
}
//罪犯
static class Culprit{
    public synchronized void say(Police p){//说
        System.out.println("罪犯:你放了我,我放了人质");
        p.response();
    }
    public synchronized void response(){//回应
        System.out.println("罪犯被放了,罪犯也放了人质");
    }
}
//警察
static class Police{
    public synchronized void say(Culprit c){
        System.out.println("警察:你放了人质,我放了你");//警察说了话,需要罪犯的回应
        c.response();
    }
    public synchronized void response(){
        System.out.println("警察救到了人质,但是罪犯跑了")
    }
}

出现死锁的情况:

Java多线程技术概述(知识点整理)

没有出现死锁的情况:

Java多线程技术概述(知识点整理)

多线程通信问题

Object类里的notify()、notifyAll()、wait()方法。

生产者与消费者

就像厨师和服务员,厨师在做饭的时候,服务员是在休息的,等厨师做好了,服务员被唤醒,而厨师进入休息状态。

确保生产者在生产时消费者没有进行消费。

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);
    }
}

Java多线程技术概述(知识点整理)

造成上述错乱的原因是厨师做食物Food的时候刚给食物赋名字name,就被服务员端走了,而味道taste有可能是上一个对象遗留的。这就是两个线程合作时出现的不协调现象。

而如果把食物的两个方法定义成同步方法,会造成更加错乱的现象,有可能,厨师做完了第一顿饭,有可能服务员还没来得及端,厨师就做完了第二顿饭,那么第一顿饭就没了。即厨师做饭做快了,或者服务员端菜端快了。

解决方法:厨师干活的时候,服务员歇着;厨师歇着的时候,服务员干活

static class Food{
    private String name;
    private String taste;
    private boolean flag = true;//饭菜是否做好
    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();//唤醒在当前this下睡着的所有线程
            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();
            }
        }
    }
}

Java多线程技术概述(知识点整理)

线程的六种状态

线程状态。线程可以处于以下状态之一:

  • NEW
    尚未启动的线程处于此状态。
  • RUNNABL
    在Java虚拟机中执行的线程处于此状态。
  • BLOCKED
    被阻塞等待监视器锁定的线程处于此状态。
  • WAITING
    无限期等待另一个线程执行特定操作的线程处于此状态。
  • TIMED_WAITING
    正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。
  • TERMINATED
    已退出的线程处于此状态。

线程在给定时间点只能处于一种状态。 这些状态是虚拟机状态,不反映任何操作系统线程状态。

Java多线程技术概述(知识点整理)

特殊的创建方法

Interface Callable

有两种用法:1、和主线程一起运行,和线程一样;2、主线程等待其完成

Callable和Runnable的区别

接口定义:

public interface Callable<V> {
    V call() throws Exception;
}
public interface Runnable {
    public abstract void run();
}

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();
Runnable与Callable的相同点
  • 都是接口
  • 都可以编写多线程程序
  • 都采用Thread.start()启动线程
Runnable与Callable的不同点
  • Runnable没有返回值;Callable可以返回执行结果
  • Callable接口的call()允许抛出异常;Runnable的run()不能抛出
public static void main(String[] args){
    Callable<Integer> c = new MyCallable();
    FutureTask<Integer> task = new FutureTask<>(c);
    new Thread(task).start();
    Integer j = task.get();//调用get方法会导致主线程停在这里,等待MyCallable线程执行完毕
    //task.cancel(true);//取消线程
    System.out.println("返回值:"+j);
    for(int i=0;i<10;i++){
        try{
           Thread.sleep(100); 
        } catch(InterruptedException e) {
            e.printStackTrace();
        }
    	System.out.println(i);
    }
}
static class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        //Thread.sleep(3000);
        for(int i=0;i<10;i++){
        	try{
           		Thread.sleep(100); 
        	} catch(InterruptedException e) {
           		e.printStackTrace();
        	}
            System.out.println(i);
        }
        return 100;
    }
}

线程池概述

线程操作流程:创建线程 -> 创建任务 -> 执行任务 -> 关闭线程

其中最耗费时间的是创建线程和关闭线程。

线程池Executors

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。

线程池的好处
  • 降低资源的消耗;
  • 提高响应速度;
  • 提高线程的可管理性;
线程池原理图

Java多线程技术概述(知识点整理)

Java中的四种线程池 .ExecutorService
1、缓存线程池

特点:长度无限制。

执行流程:

  1. 判断线程池是否存在空闲线程
  2. 存在则使用
  3. 不存在,则创建线程并放入线程池,然后使用
public static void main(String[] args){
    ExecutorService service = Executor.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、定长线程池

特点:长度是指定的数值

执行流程:

  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());
    	}
	});
	service.execute(new Runnable() {
    	@Override
    	public void run() {
    		System.out.println("线程的名称:"+Thread.currentThread().getName());
    	}
	});
}
3、单线程线程池

特点:效果与定长线程池创建时传入数值1效果一样。

执行流程:

  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());
        }
    });
}

4、周期性任务定长线程池

执行流程:

  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,1,TimeUnit.SECONDS);

Lambda表达式

函数式编程思想

面向对象:创建对象调用方法解决问题。

Lambda表达式关注的是对象的方法。

//冗余的Runnable代码
public static void main(String[] args) {
    MyRunnable r = new MyRunnable();
    Thread t = new Thread(r);
    t.start();
}
public static class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("锄禾日当午");
    }
}
//改善之后的Runnable代码
public static void main(String[] args) {
    Thread t = new Thread(new Runnable() {
        @Override
    	public void run() {
        	System.out.println("锄禾日当午");
    	}
    });
    t.start();
}
//使用Lambda表达式改善Runnable代码
public static void main(String[] args) {
    Thread t = new Thread(() -> System.out.println("锄禾日当午"));//效果和上述代码一致
    //Thread t = new Thread(() -> {System.out.println("锄禾日当午");});
    t.start();
}

//自定义接口测试
public static void main(String[] args) {
    /*print(new MyMath() {
        @Override
        public int sum(int x,int y) {
            return x+y;
        }
    },100,200);*/
    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);
}
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。