创建线程任务

文章目录

1. 创建线程

Java中创建线程的方式有两种:

  • 实现 java.lang.Runnable 接口
  • 继承 java.lang.Thread
1.1 通过Runnable接口创建线程
public class MyRunnable implements Runnable {    
      @Override    
      public void run() {        
            Printer.print("MyRunnable.run() start. start time = " + Timer.getTime());
            Printer.print("current thread id = " + Thread.currentThread().getId());
            try {            
                  Thread.sleep(5000);       
            } catch (InterruptedException e){            
                  e.printStackTrace();        
            }        
            Printer.print("MyRunnable.run() finished. finish time = " + Timer.getTime());
      }
}
1.2 通过Thread类创建线程
public class MyThread extends Thread{    
      @Override    
      public void run() {        
            Printer.print("MyThread.run() start. start time = " + Timer.getTime());
            Printer.print("current thread id = " + Thread.currentThread().getId());
            super.run();        
            try {            
                    sleep(2000);       
            } catch (InterruptedException e){            
                    e.printStackTrace();        
            }        
            Printer.print("MyThread.run() finished. finish time = " + Timer.getTime());
      }
}
1.3 实例说明

实例化 1.1 和 1.2 中创建的线程并调用其执行方法

public static void main(String[] args){
      MyThread thread = new MyThread();    
      MyRunnable runnable = new MyRunnable();        
      thread.start();    
      runnable.run();
}

输出的内容如下:

MyRunnable.run() start. start time = 2019-01-26 12:11:18
current thread id = 1
MyThread.run() start. start time = 2019-01-26 12:11:18
current thread id = 11
MyThread.run() finished. finish time = 2019-01-26 12:11:20
MyRunnable.run() finished. finish time = 2019-01-26 12:11:23

可以看到MyThread和MyRunnable并不是一个执行完再执行另一个,而是同时执行的,并且两个任务所在的线程id也不一样,可以证明确实是在不同的线程中执行。

Thread 和 Runnable 的区别是显而易见的,它们一个是类一个是接口,实际使用中选择哪一个来创建新线程则要看具体需求。


2. 返回结果的线程任务 Callable 和 Future

假如我们我执行一个多线程任务,并希望任务完成之后返回结果值,这时候用Thread或Runnable就无法实现这个目的,因为它们只能用来执行任务,无法返回值。而Callable和Future就是为了实现返回值而设计的,下面就来详细介绍它们的用法。

2.1 Callable接口
2.1.1 Callable与Runnable的区别
  • 实现Callable接口需实现其call()方法,call()方法会返回一个结果值,Runnable接口的run()方法不会返回值
  • Callable接口并不能用来创建一个新线程,Runnable接口可以
  • Callable接口的call()方法可能会抛出异常,而Runnable接口的run()方法不会
2.1.2 实现Callable接口的例子
public class MyCallable implements Callable<String> {    

      private int sleepTime;
      
      public MyCallable(int sleepTime) {    
            this.sleepTime = sleepTime;
      }

      @Override    
      public String call() {        
            try {            
                    Thread.sleep(sleepTime);       
             } catch (InterruptedException e){            
                    e.printStackTrace();        
             }        
            return Thread.currentThread().getName() + " : " + Timer.getTime();
      }
}

Callable接口接受一个类型参数,指定call()方法的返回值类型。如果不指定,call()方法的返回值类型默认为Object。


2.2 Future接口

call()方法完成之后,结果需要被保存在一个存在于主线程的对象之中,以让主线程获取结果。要实现这一目标,需要使用Future。Future用来保存call()方法返回的结果,有了Future,就能在主线程中观测其它线程的任务执行进度并获取其返回值。

要实现Future接口,需要重写它的以下5个方法:

  • boolean cancel(boolean mayInterrupt)
    这个方法用来取消任务,如果任务尚未开始,它将直接终止任务。如果任务已经开始,它将根据 mayInterrupt来决定是否终止任务。

  • Object get() throws InterruptedException, ExecutionException
    用来获取任务的结果值,如果任务已经完成,它将立即返回结果值,否则它将阻塞线程,直到任务完成并返回结果值。

  • Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
    这个方法的目的和get()一样,不同之处在于它接受一个超时时长,表示其最大阻塞时长,如果时间到了还没有返回结果,它将抛出TimeoutException异常。

  • boolean isDone()
    如果任务完成,或者以其它任何方式终止(比如抛了异常),都将返回true,否则返回false。

  • boolean isCanceled()
    如果任务在完成之前被取消了,将返回true。

2.2.1 FutureTask

Java已经为我们封装好了一个实现类FutureTask,它同时实现了Runnable接口和Future接口,可以直接拿来用,不用自己去实现Future接口了。

写一个用FutureTask实现获取其它线程任务执行结果的例子:

private static void testFutureTask() {       
      MyCallable callable1 = new MyCallable(1000);        
      MyCallable callable2 = new MyCallable(2000);        
      //FutureTask初始化的时候接受Callable作为参数
      FutureTask<String> futureTask1 = new FutureTask<>(callable1);        
      FutureTask<String> futureTask2 = new FutureTask<>(callable2);

//    ExecutorService executor = Executors.newFixedThreadPool(2);
//    executor.execute(futureTask1);
//    executor.execute(futureTask2);        
//   新开线程执行FutureTask任务,也可以使用上面的线程池方式
      new Thread(futureTask1).start();        
      new Thread(futureTask2).start();        
      
      while (true) {            
            try {                
                    if (futureTask1.isDone() && futureTask2.isDone()) {                    
                            System.out.println("Done");                    
                            //shut down executor service//                    
                            //executor.shutdown();                    
                            return;               
                    }               
                    if (!futureTask1.isDone()) {                   
                    //wait indefinitely for future task to complete                    
                            System.out.println("FutureTask1 output=" + futureTask1.get());                
                    }               
                    System.out.println("Waiting for FutureTask2 to complete");                
                    String s = futureTask2.get(200L, TimeUnit.MILLISECONDS);                
                    if (s != null) {                    
                            System.out.println("FutureTask2 output=" + s);                
                    }            
            } catch (InterruptedException | ExecutionException e) {                
                    e.printStackTrace();            
            } catch (TimeoutException e) {                
                    //do anything           
            }       
      }    
}

执行上面的代码,打印的内容如下:

FutureTask1 output=Thread-0 : 2019-01-26 18:02:08
Waiting for FutureTask2 to complete
Waiting for FutureTask2 to complete
Waiting for FutureTask2 to complete
Waiting for FutureTask2 to complete
Waiting for FutureTask2 to complete
FutureTask2 output=Thread-1 : 2019-01-26 18:02:09
Done

3. 定时线程任务 Timer 和 TimerTask

TimerTask 是一个实现了 Runnable接口的抽象类。Timer则可以创建一个新线程,来定时执行任务,或者重复执行任务,用起来非常简单。

先看一个小例子:

Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {    
      @Override    
      public void run() {        
            Printer.print("msg from timer task:  " + TimeUtil.getTime());    
      }
}, 0, 1000);

这段代码使用Timer每隔一秒钟重复输出一句话,输出结果如下:

msg from timer task: 2019-01-30 16:50:49
msg from timer task: 2019-01-30 16:50:50
msg from timer task: 2019-01-30 16:50:51
msg from timer task: 2019-01-30 16:50:52
msg from timer task: 2019-01-30 16:50:53
msg from timer task: 2019-01-30 16:50:54

下面介绍下Timer的几个方法:

  • public void schedule(TimerTask task, long delay) : 用来延时执行任务
  • public void schedule(TimerTask task, Date time) : 用来定时执行任务
  • public void schedule(TimerTask task, long delay, long period) : 重复执行一个任务
  • public void schedule(TimerTask task, Date firstTime, long period) : 重复执行一个任务
  • public void scheduleAtFixedRate(TimerTask task, long delay, long period) : 时间控制更加精准
  • public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) : 时间控制更加精致

上一篇:Java基础学习总结:多线程之(一)并发和并行,线程和进程,线程的三种创建方式,Runnable 和 Callable 的区别


下一篇:java – Android Callable