Runnable和Callable

Runnable和Callable都可以在主线程之外单独启一个线程。

Runnable和Callable的区别在于:

使用方式:Runnable可直接使用  new Thread(Runnable runnable).start();来启动一个线程

Callable需要使用FutureTask进行封装,使用new Thread(new FutureTask<>(Callable  callable)).start();来启动一个线程

返回值:Runnable只是单纯的启动一个线程任务,这个线程任务(run方法)不能返回结果给主线程

Callable在启一个线程任务的同时可以通过Future对线程(call方法)的运行结果进行返回

异常处理:Runnable不支持抛出异常,所有异常只能在run方法内消化。

Callable支持异常抛出,可以将call方法内部的异常抛出。指定注意的是抛出的异常并不是抛到.start()方法上去了,而是抛到用来获得返回结果的FutureTask.get()方法上。

阻塞:Runnable不会阻塞主线程的运行。

Callable分两种情况。一种是不获得call方法返回结果即不调用FutureTask.get()的情况,此情况下在阻塞问题上与Runnable无明显区别。另一种情况是需要获得call方法返回结果即调用FutureTask.get()方法的情况,此种情形下如果再运行FutureTask.get()方法时对应的Callable线程还未执行完毕,那么主线程将会被阻塞,直到Callable线程的call方法执行完毕并将结果返回给主线程之后,主线程才会继续执行,因此此种情形下一般使用线程控制类CountDownLatch来控制线程来避免主线程因为一些问题被长时间阻塞。

下面通过一小段代码来验证以上结论:

package sync;


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


public class RunnableAndCallableTest {


    public static void main(String[] args) {
        long t1 = System.currentTimeMillis();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("This Is Runnable");
            }
        };


        new Thread(runnable).start();  // 
        long t2 = System.currentTimeMillis();
        // 以上代码明显不会运行1s,因此若是运行超过1s则可值阻塞了主线程
        System.out.println("runnable是否阻塞主线程:" + String.valueOf((t2 - t1) > 1000));
        
        Callable c = new Callable() {
            
            @Override
            public String call() throws Exception {
                Thread.sleep(1000);
                System.out.println("This Is Callable");
                return "The ruturn of Callable";
            }
        };
        
        Callable c2 = new Callable() {
            
            @Override
            public String call() throws Exception {
                int i = 0;
                i= 1/i; // 异常向外抛出
                return "The return of Exception Callable";
            }
        };
        // 对Callable进行封装
        FutureTask task = new FutureTask<>(c);
        FutureTask task2 = new FutureTask<>(c2);
        long t3 = System.currentTimeMillis();
        // 启动线程
        try {
            new Thread(task).start(); 
            new Thread(task2).start();
            System.out.println("异常未抛到start方法上");
        } catch (Exception e) {
            System.out.println("异常抛到了start方法上");
        }
        long t4 = System.currentTimeMillis();
        // 大于1s的话可以认为主线程阻塞到start方法上了
        System.out.println("主线程阻塞在start方法上:" + String.valueOf(t4 - t3 > 1000));
        
        try {
            System.out.println(task.get());
            System.out.println(task2.get());
            System.out.println("异常未抛到了get方法上");
        } catch (Exception e) {
            System.out.println("异常抛到了get方法上");
        }
        long t5 = System.currentTimeMillis();
        // 大于1s的话可以认为主线程阻塞到get方法上了
        System.out.println("主线程阻塞在get方法上:" + String.valueOf(t5 - t4 > 1000));
    }
}

执行结果:

runnable是否阻塞主线程:false
异常未抛到start方法上
主线程阻塞在start方法上:false
This Is Runnable
This Is Callable
The ruturn of Callable
异常抛到了get方法上
主线程阻塞在get方法上:true

参考:

说说Runnable与Callable

Java线程(七):Callable和Future

你可能感兴趣的:(JAVA)