精华内容
参与话题
问答
  • Runnable 和Callable区别

    千次阅读 2018-05-06 15:43:55
    Java 提供了三种创建线程的...通过Callable和Future创建线程 通过实现 Runnable 接口来创建线程 public class RunnableDemo { public static void main(String[] args) { new Thread(new MyThr...

    Java 提供了三种创建线程的方法

    1. 通过实现Runnable接口
    2. 通过继承Thread接口
    3. 通过Callable和Future创建线程

    通过实现 Runnable 接口来创建线程

    public class RunnableDemo {
        public static void main(String[] args) {
            new Thread(new MyThread(),"线程1").start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    
                }
            },"线程2").start();
        }
    }
    
    /**
     * 实现 Runnable 接口的线程类
     */
    class MyThread implements Runnable{
    
        /** 
         * 重写run方法
         */
        @Override
        public void run() {
            // TODO Auto-generated method stub
            
        }
    }
    

    通过继承Thread类

    public class ThreadDemo {
        public static void main(String[] args) {
            new MyThread().start();
            new Thread(new MyThread(), "线程2").start();
        }
    }
    class MyThread extends Thread{
    
        /** 
         * 重写run方法
         */
         @Override
         public void run() {
             // TODO Auto-generated method stub
             super.run();
         }
    }
    

    通过Callable和Future创建线程

    public class FutureDemo {
        public static void main(String[] args) {
            //创建 Callable 实现类的实例
            MyCallable myCallable = new MyCallable();
            //使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值
            FutureTask<String> futureTask = new FutureTask<String>(myCallable);
            String res = null;
            try {
                //使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程
                //没这句,下句代码获取不到结果,会一直等待执行结果
                new Thread(futureTask,"线程1").start();
                //调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值
                res = futureTask.get();
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(res);
        }
    }
    /**
     * 创建 Callable 接口的实现类,并实现 call() 方法
     */
    class MyCallable implements Callable<String>{
    
        /** 
         * 该 call() 方法将作为线程执行体,并且有返回值
         */
        @Override
        public String call() throws Exception {
            return "success";
        }
    }
    

    Runnable 和Callable区别

    方法名 ExecutorService的执行方法 ExecutorService.submit()返回值 返回的Future调用get()方法 线程体 取消执行
    Runnable execute和submit Future null run() 不能
    Callable 只能是submit Future Future定义的泛型T call() Future.cancel能取消执行
    展开全文
  • Callable接口详解

    千次阅读 2019-02-26 10:50:51
    Callable接口详解 Callable: 返回结果并且可能抛出异常的任务。 优点: 可以获得任务执行返回值; 通过与Future的结合,可以实现利用Future来跟踪异步计算的结果。 Runnable和Callable的区别: 1、Callable...

    Callable接口详解

    • Callable: 返回结果并且可能抛出异常的任务。
    • 优点:
      • 可以获得任务执行返回值;
      • 通过与Future的结合,可以实现利用Future来跟踪异步计算的结果。

    Runnable和Callable的区别:

    • 1、Callable规定的方法是call(),Runnable规定的方法是run().
    • 2、Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
    • 3、call方法可以抛出异常,run方法不可以
    • 4、运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
    • 5、代码示例:
    •   //Callable 接口
        public interface Callable<V> {
           V call() throws Exception;
        }
        // Runnable 接口
        public interface Runnable {
            public abstract void run();
        }
      

    Future接口

    • Future是一个接口,代表了一个异步计算的结果。接口中的方法用来检查计算是否完成、等待完成和得到计算的结果。
    • 当计算完成后,只能通过get()方法得到结果,get方法会阻塞直到结果准备好了。
    • 如果想取消,那么调用cancel()方法。其他方法用于确定任务是正常完成还是取消了。
    • 一旦计算完成了,那么这个计算就不能被取消。

    FutureTask类

    • FutureTask类实现了RunnableFuture接口,而RunnnableFuture接口继承了Runnable和Future接口,所以说FutureTask是一个提供异步计算的结果的任务。
    • FutureTask可以用来包装Callable或者Runnbale对象。因为FutureTask实现了Runnable接口,所以FutureTask也可以被提交给Executor(如上面例子那样)。

    Callable两种执行方式

    • 1、借助FutureTask执行
      • FutureTask类同时实现了两个接口,Future和Runnable接口,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
      • 具体流程:
        •   //定义实现Callable接口的的实现类重写call方法。
            public class MyCallableTask implements Callable<Integer>{
                @Override
                    public Integer call() throws Exception {
                       //TODO 线程执行方法
                    }
            }
            ---------------------------------------------------------
            //创建Callable对象
            Callable<Integer> mycallabletask = new MyCallableTask();
            //开始线程
            FutureTask<Integer> futuretask= new FutureTask<Integer>(mycallabletask);
            new Thread(futuretask).start();
            --------------------------------------------------------
            通过futuretask可以得到MyCallableTask的call()的运行结果:
            futuretask.get();
          
    • 2、借助线程池来运行
      • 线程池中执行Callable任务原型:
        •   public interface ExecutorService extends Executor {
                //提交一个Callable任务,返回值为一个Future类型
                <T> Future<T> submit(Callable<T> task);
          
                    //other methods...
            }
          
      • 借助线程池来运行Callable任务的一般流程为:
        •    ExecutorService exec = Executors.newCachedThreadPool();
             Future<Integer> future = exec.submit(new MyCallableTask());
          
        • 通过future可以得到MyCallableTask的call()的运行结果: future.get();

    举例说明

    • 例1:
      •   public class CallableTest {
              public static void main(String[] args) throws ExecutionException, InterruptedException,TimeoutException{
                  //创建一个线程池
                  ExecutorService executor = Executors.newCachedThreadPool();
                  Future<String> future = executor.submit(()-> {
                          TimeUnit.SECONDS.sleep(5);
                          return "CallableTest";
                  });
                  System.out.println(future.get());
                  executor.shutdown();
              }
          }
        
    • 例2:Callable任务借助FutureTask运行:
      •   public class CallableAndFutureTask {
              Random random = new Random();
              public static void main(String[] args) {
                  Callable<Integer> callable = new Callable<Integer>() {
                      public Integer call() throws Exception {
                          return random.nextInt(10000);
                      }
                  };
                  FutureTask<Integer> future = new FutureTask<Integer>(callable);
                  Thread thread = new Thread(future);
                  thread.start();
                  try {
                      Thread.sleep(2000);
                      System.out.println(future.get());
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          }
        
    • 例3:Callable任务和线程池一起使用,然后返回值是Future:
      •    public class CallableAndFuture {
               Random random = new Random();
               public static void main(String[] args) {
                   ExecutorService threadPool = Executors.newSingleThreadExecutor();
                   Future<Integer> future = threadPool.submit(new Callable<Integer>() {
                       public Integer call() throws Exception {
                           return random.nextInt(10000);
                       }
                   });
                   try {
                       Thread.sleep(3000);
                       System.out.println(future.get());
                   } catch (Exception e) {
                       e.printStackTrace();
                   }
               }
           }
        
    • 例4:当执行多个Callable任务,有多个返回值时,我们可以创建一个Future的集合:
      •   class MyCallableTask implements Callable<String> {
              private int id;
              public OneTask(int id){
                  this.id = id;
              }
              @Override
              public String call() throws Exception {
                  for(int i = 0;i<5;i++){
                      System.out.println("Thread"+ id);
                      Thread.sleep(1000);
                  }
                  return "Result of callable: "+id;
              }
          }
          public class Test {
        
              public static void main(String[] args) {
                  ExecutorService exec = Executors.newCachedThreadPool();
                  ArrayList<Future<String>> results = new ArrayList<Future<String>>();
        
                  for (int i = 0; i < 5; i++) {
                      results.add(exec.submit(new MyCallableTask(i)));
                  }
        
                  for (Future<String> fs : results) {
                      if (fs.isDone()) {
                          try {
                              System.out.println(fs.get());
                          } catch (Exception e) {
                              e.printStackTrace();
                          }
                      } else {
                          System.out.println("MyCallableTask任务未完成!");
                      }
                  }
                  exec.shutdown();
              }
          }
        

    StopWatch的使用

    • Spring提供的计时器StopWatch对于秒、毫秒为单位方便计时的程序,尤其是单线程、顺序执行程序的时间特性的统计输出支持比较好。也就是说假如我们手里面有几个在顺序上前后执行的几个任务,而且我们比较关心几个任务分别执行的时间占用状况,希望能够形成一个不太复杂的日志输出,StopWatch提供了这样的功能。而且Spring的StopWatch基本上也就是仅仅为了这样的功能而实现。
    •   public String call() throws Exception {
        	StopWatch stopWatch = new StopWatch();
        	stopWatch.start("测试StopWatch");
        	//TODO 业务逻辑
        	stopWatch.stop();
        	return "test";
        }
      
        
      
    展开全文
  • Callable和Future 的用法详解

    千次阅读 2018-01-31 18:46:49
    接着上一篇继续并发包的学习,本篇说明的是Callable和Future,它俩很有意思的,一个产生结果,一个拿到结果。 Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回...

    接着上一篇继续并发包的学习,本篇说明的是Callable和Future,它俩很有意思的,一个产生结果,一个拿到结果。
    Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值,下面来看一个简单的例子:

    public class CallableAndFuture {
        public static void main(String[] args) {
            Callable<Integer> callable = new Callable<Integer>() {
                public Integer call() throws Exception {
                    return new Random().nextInt(100);
                }
            };
            FutureTask<Integer> future = new FutureTask<Integer>(callable);
            new Thread(future).start();
            try {
                Thread.sleep(5000);// 可能做一些事情
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

    FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值,那么这个组合的使用有什么好处呢?假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到,岂不美哉!这里有一个Future模式的介绍:http://openhome.cc/Gossip/DesignPattern/FuturePattern.htm
    下面来看另一种方式使用Callable和Future,通过ExecutorService的submit方法执行Callable,并返回Future,代码如下:

    public class CallableAndFuture {
        public static void main(String[] args) {
            ExecutorService threadPool = Executors.newSingleThreadExecutor();
            Future<Integer> future = threadPool.submit(new Callable<Integer>() {
                public Integer call() throws Exception {
                    return new Random().nextInt(100);
                }
            });
            try {
                Thread.sleep(5000);// 可能做一些事情
                System.out.println(future.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

    代码是不是简化了很多,ExecutorService继承自Executor,它的目的是为我们管理Thread对象,从而简化并发编程,Executor使我们无需显示的去管理线程的生命周期,是JDK 5之后启动任务的首选方式。
    执行多个带返回值的任务,并取得多个返回值,代码如下:

    public class CallableAndFuture {
        public static void main(String[] args) {
            ExecutorService threadPool = Executors.newCachedThreadPool();
            CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);
            for(int i = 1; i < 5; i++) {
                final int taskID = i;
                cs.submit(new Callable<Integer>() {
                    public Integer call() throws Exception {
                        return taskID;
                    }
                });
            }
            // 可能做一些事情
            for(int i = 1; i < 5; i++) {
                try {
                    System.out.println(cs.take().get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        }
    } 
      其实也可以不使用CompletionService,可以先创建一个装Future类型的集合,用Executor提交的任务返回值添加到集合中,最后遍历集合取出数据,代码略。更新于2016-02-05,评论中就这个说法引发了讨论,其实是我没有讲清楚,抱歉。这里再阐述一下:提交到CompletionService中的Future是按照完成的顺序排列的,这种做法中Future是按照添加的顺序排列的。所以这两种方式的区别就像评论中fishjam所描述的那样。
    
       本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/7451464。
    
    展开全文
  • Java线程(七):Callable和Future

    万次阅读 多人点赞 2016-02-05 15:12:04
    接着上一篇继续并发包的学习,本篇说明的是Callable和Future,它俩很有意思的,一个产生结果,一个拿到结果。 Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回...

           接着上一篇继续并发包的学习,本篇说明的是Callable和Future,它俩很有意思的,一个产生结果,一个拿到结果。
           Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值,下面来看一个简单的例子:

    public class CallableAndFuture {
    	public static void main(String[] args) {
    		Callable<Integer> callable = new Callable<Integer>() {
    			public Integer call() throws Exception {
    				return new Random().nextInt(100);
    			}
    		};
    		FutureTask<Integer> future = new FutureTask<Integer>(callable);
    		new Thread(future).start();
    		try {
    			Thread.sleep(5000);// 可能做一些事情
    			System.out.println(future.get());
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		} catch (ExecutionException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

           FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值,那么这个组合的使用有什么好处呢?假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到,岂不美哉!这里有一个Future模式的介绍:http://openhome.cc/Gossip/DesignPattern/FuturePattern.htm。
           下面来看另一种方式使用Callable和Future,通过ExecutorService的submit方法执行Callable,并返回Future,代码如下:

    public class CallableAndFuture {
    	public static void main(String[] args) {
    		ExecutorService threadPool = Executors.newSingleThreadExecutor();
    		Future<Integer> future = threadPool.submit(new Callable<Integer>() {
    			public Integer call() throws Exception {
    				return new Random().nextInt(100);
    			}
    		});
    		try {
    			Thread.sleep(5000);// 可能做一些事情
    			System.out.println(future.get());
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		} catch (ExecutionException e) {
    			e.printStackTrace();
    		}
    	}
    }
    

           代码是不是简化了很多,ExecutorService继承自Executor,它的目的是为我们管理Thread对象,从而简化并发编程,Executor使我们无需显示的去管理线程的生命周期,是JDK 5之后启动任务的首选方式。
           执行多个带返回值的任务,并取得多个返回值,代码如下:

    public class CallableAndFuture {
    	public static void main(String[] args) {
    		ExecutorService threadPool = Executors.newCachedThreadPool();
    		CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);
    		for(int i = 1; i < 5; i++) {
    			final int taskID = i;
    			cs.submit(new Callable<Integer>() {
    				public Integer call() throws Exception {
    					return taskID;
    				}
    			});
    		}
    		// 可能做一些事情
    		for(int i = 1; i < 5; i++) {
    			try {
    				System.out.println(cs.take().get());
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			} catch (ExecutionException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    } 
    

           其实也可以不使用CompletionService,可以先创建一个装Future类型的集合,用Executor提交的任务返回值添加到集合中,最后遍历集合取出数据,代码略。更新于2016-02-05,评论中就这个说法引发了讨论,其实是我没有讲清楚,抱歉。这里再阐述一下:提交到CompletionService中的Future是按照完成的顺序排列的,这种做法中Future是按照添加的顺序排列的。所以这两种方式的区别就像评论中fishjam**所描述的那样。

           本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/7451464,转载请注明。

    展开全文
  • java Callable 实现原理分析

    万次阅读 2018-05-31 15:50:23
    前言 我们常用的创建线程方式一般有下面 2 种: 继承Thread,重写run方法 实现Runnable接口,重新...虽然是实现 Callable ,但是在 Executor 实际运行时,会将 Runnable 的实例或 Callable 的实例转化为 Runnable...
  • JUC详解-6-Callable

    2020-11-14 00:26:21
    Callable Callable接口 可以有返回值 可以抛出异常 方法不同,call() new Thread().start(); 如何启动Callable Thread只接收Runnable 代码测试 import java.util.concurrent.Callable; import java....
  • Callable实现多线程

    千次阅读 2019-05-05 22:48:16
    运行下面的代码,我们会发现,I'm T..... 和 I'm T0.....会交替输出。通过下面的代码我们可以知道,这种方式相对于Runnable的实现方式...1、Runnable接口复写的是run方法,而Callable接口复写的是call方法 2、Runn...
  • 2.1 使用Callable线程步骤: 3. 代码设计分析 4. 深入Callable具体源码,一步步查看如何执行: 4.1 Callbale接口---FutureTask构造方法的接口 4.2 FutureTask类和内部重要方法 4.3 RunnableFuture接口---继承了...
  • Callable与Future的介绍

    2018-08-01 13:36:55
    Callable与Future的介绍 Callable与 Future 两功能是Java在后续版本中为了适应多并法才加入的,Callable是类似
  • 深入理解Callable

    万次阅读 2017-08-22 18:34:42
    概述Callable和Runnbale一样代表着任务,区别在于Callable有返回值并且可以抛出异常。其使用如下: public class CallableDemo { static class SumTask implements Callable<Long> { @Override public Long ...
  • Callable接口

    千次阅读 2019-06-18 17:38:11
    一、创建多线程的方式 ...第三种:Callable接口 直接看代码吧: /* * Copyright (C), 2013-2019, 天津大海云科技有限公司 */ import com.jikang.MyApplication; import org.junit.runner.RunWith; import ...
  • Runnable和Callable的区别和用法

    万次阅读 2017-11-11 00:32:42
    创建线程的四种方式 Java多线程实现方式主要有四种:继承Thread类、实现Runnable接口、实现Callable接口通过FutureTask包装器来创建Thread线程、使用ExecutorService、Callable、Future实现有返回结果的多线程。
  • Java中实现多线程有3种方法:继承Thread类实现Runnable接口实现Callable接口(参考&lt;Java编程思想(第4版)&gt; 21.2.4章节,原来一直以为是2种,后来发现是3种)回到顶部第一种实现方法—继承Thread类继承...
  • Runnable和Callable区别

    千次阅读 2019-03-04 11:14:14
    Runnable和Callable区别Runnable和Callable区别:ExcutorService中的excutorsubmit方法的区别使用场景:FutureTask:ExecutorCompletionService:示例代码:使用CompletionService维护结果:自己创建list维护...
  • java中Runnable和Callable区别

    万次阅读 2020-03-11 09:18:36
    java中Runnable和Callable区别 在java的多线程开发中Runnable一直以来都是多线程的核心,而Callable是java1.5添加进来的一个增强版本。 本文我们会详细探讨Runnable和Callable区别。 运行机制 首先看下Runnable...
  • Runnable和Callable区别和联系

    千次阅读 2018-08-20 22:37:30
    Java多线程有两个重要的接口,Runnable和Callable,分别提供一个run方法call方法,二者是有较大差异的。 1)Runnable提供run方法,无法通过throws抛出异常,所有CheckedException必须在run方法内部处理。Callable...
  • runnablecallable区别

    2019-04-03 20:02:35
    最直接的就是:runnable是通过run方法实现多线程操作 而callable是通过call方法实现多线程操作实体 有返回值的任务必须通过callable接口实现 而没有返回值的任务通过runnable实现 ,执行callable任务后,可以获取...
  • Runnable Callable 有什么区别

    千次阅读 2019-05-13 17:12:54
    Runnable Callable 有什么区别? 主要区别 Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回值,支持泛型 Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call ...
  • 主要区别 Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回值,支持泛型 Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出异常,可以获取异常信息 ...
  • Callable和Runnable区别

    千次阅读 2019-05-06 20:51:12
    class c implements Callable<String>{ @Override public String call() throws Exception { return null; } } class r implements Runnable{ @Override public void run() { } } 相同点...
  • 1,Callable call方法 可以有返回值,Runnable 的run 方法不可以, 2,Callable异常可以抛出,Runnable 不能上抛 3,Callable可以返回装载有计算结果的Future对象 Callable工作的Demo: package ...
  • 1、Runnable  Runnable是一个接口,没有返回也不能抛出异常。定义如下 public interface Runnable { public abstract void run(); }  举个栗子,开启一个线程,休眠3秒。这里定义了一个名为OneRun类,并且...
  • 介绍java 中 Runnable Callable 从java早期开始,多线程已经就是其主要特性之一。Runable接口是表现多线程任务核心接口,Callable是java1.5之后引入的新街口。 本文,我们探讨下这两个接口之间的差别。 ## 执行...
  • 我们知道实现Runnable和实现Callable均可创建线程,那么他们有什么区别呢? Runnable和Callable区别 Runnable接口 public interface Runnable { void run(); }   Callable接口 public interface ...
  • Java中的RunnableCallable、Future、FutureTask的区别与示例
  • Runnable @FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread ...
  • Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;...Callable接口中的call()方法是有返回值的,是一个泛型,Future、FutureTask配合可以用来获取异步执行的结果。 ...
  • 3:通过Callable和Future创建线程 相同点 都是接口 都可以编写多线程程序 都采用Thread.start()启动线程 不同点 (1)Callable规定的方法是call(),Runnable规定的方法是run()。其中Runnable可以提交给Thread来...
  • Runnable和Callable接口的区别

    千次阅读 2016-08-28 14:48:08
    线程内容回顾 -------------------------------------------------------------- ...(Thread,Runnable,synchronized) 2.Android 中线程消息模型?(Message,MessageQueue,Looper,Handler,HandlerThread) 3.Andro
  • Java线程——CallableRunnable区别

    万次阅读 多人点赞 2018-08-06 18:13:35
    Runnable 其中Runnable应该是我们最熟悉的接口,它只有一个run()函数,用于将耗时操作写在其中,该函数没有返回值。然后使用某个线程去执行该runnable即可实现多线程,Thread类在调用start()函数后就是执行的是...

空空如也

1 2 3 4 5 ... 20
收藏数 107,327
精华内容 42,930
关键字:

callable