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

前言

线程的实现主要有三种方式:继承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 即可获得当前线程。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值