您的位置:首页 > 编程语言 > Java开发

java多线程(2):线程的实现

2018-03-30 19:52 260 查看

前言

线程的实现主要有三种方式:继承Thread类,实现Runnable接口,实现Callable接口和Future获取返回值。

继承Thread类

继承Thread类须重写run()方法。如果没有重写run()方法,那这个线程类没有任何执行方法。public class MyThread extends Thread{

@Override
public void run() {
System.out.println("子线程ID:"+Thread.currentThread().getId());
}

}创建好自己的线程类之后,就可以创建线程对象了。再启动线程对象的start()方法,就另起了一个线程。这里需要注意到,不是调用run()方法启动线程,run()方法中只是定义需要执行的任务,如果调用run()方法,即相当于在主线程中执行run方法,跟普通的方法调用没有任何区别,此时并不会创建一个新的线程来执行定义的任务。public static void main(String[] args) {
    System.out.println("主线程ID:"+Thread.currentThread().getId());
    MyThread thread1 = new MyThread("thread1");
    thread1.start();
    MyThread thread2 = new MyThread("thread2");
    thread2.run();
}上面方法执行的结果是:主线程ID:1
线程:thread2 子线程ID:1
线程:thread1 子线程ID:10可以看到thread1调用start()方法后,ID与主线程不一致,说明是另起了一个线程在执行。thread2调用run()方法后,ID与主线程保持一致,说明仍是主线程在执行,没有另起线程。
此外,虽然thread1的start()方法调用在thread2的run()方法前面调用,但是先输出的是thread2的run()方法调用的相关信息,说明新线程创建的过程不会阻塞主线程的后续执行。

实现Runnable接口

实现Runnable接口必须重写其run方法。其实Thread类也是实现了Runnable接口。
通过实现Runnable接口,我们定义了一个子任务,然后将子任务交由Thread去执行。public class Thread implements Runnable 如果直接调用Runnable的run方法是不会新创建一个线程的。下面的示例说明子线程和主线程是一个ID,即没有另起线程。
比如:public class MyRunnable implements Runnable{

@Override
public void run() {
System.out.println("子线程ID:"+Thread.currentThread().getId());
}

}
System.out.println("主线程ID:"+Thread.currentThread().getId());
MyRunnable myRunnable = new MyRunnable();
myRunnable.run();
运行结果是:主线程ID:1
子线程ID:1我们需要将实现runnable接口的类以参数的形式传给Thread类,然后调用Thread类的start()方法。 public static void main(String[] args) {
System.out.println("主线程ID:"+Thread.currentThread().getId());
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}运行结果是:主线程ID:1
子线程ID:10

通过Callable和Future创建线程

Callable与上面的Runnable类似,区别在于Runnable不会返回线程的执行结果,但是Callable可以产生结果,并且这个结果可以被Future拿到。Callable用来产生结果,Future用来创建结果。
创建Callable接口的实现类,实现call()方法,该call()方法作为线程的执行体,并且有返回值。作用类似于Thread的run()方法。
创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
使用FutureTask对象作为Thread对象的target创建并启动新线程。
调用FutureTask的get()方法来获得子线程执行结束后的返回值。public class CallableThread implements Callable<Integer>{

public static void main(String[] args) {
CallableThread ct = new CallableThread();
FutureTask<Integer> ft = new FutureTask<>(ct);
// 这里是启动一个新的线程
new Thread(ft,"子线程").start();
try {
System.out.println("子线程的返回值:"+ft.get());
} catch (InterruptedException |ExecutionException e) {
e.printStackTrace();
}
}

@Override
public Integer call() throws Exception {
System.out.println("调用call()方法:"+Thread.currentThread().getName());
return 1;
}
}

三者的区别

实现Callable接口与实现Runnable接口的主要区别在于前者能取到返回值,后者取不到返回值。
实现Runnable接口相比于继承Thread类所具有的优势:主要是可避免java中的单继承,增加程序的健壮性,代码可以被多个线程共享。此外,线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
继承 Thread 类的方式相比于实现Runnable接口的优势主要在于,实现代码编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息