Java创建多线程Thread,Runnable,Callable详解


多线程

进程是指一段正在执行的程序,线程是指程序执行的最小单元。一个进程拥有多个线程,各个线程之间共享程序内存空间,但是各个线程拥有自己的栈空间。

在这里插入图片描述


一、多线程优势

操作系统级别上,程序执行是以进程为单位,每个进程的多个线程互不影响的并发执行。使用多线程的好处:

  1. 多线程可以减少程序的响应时间;
  2. 与进程相比,线程的创建和切换开销更小。启动一个新的线程必须给这个线程分配独立的地址空间,建立许多数据结构来维护线程代码段,数据段等信息,而运行于同一进程内的线程共享代码段和数据段,线程的启动或切换的开销比进程的开销要少很多。多线程在数据共享方面效率非常高;
  3. 多CPU或者多核计算机本身就具有执行多线程的能力,使用多线程可以提高CPU的利用率。
  4. 使用多线程能简化程序的结构,使程序便于理解和维护。

二、如何实现java多线程

1.继承Thread类,重写run()方法。

Thread类本质上是实现了Runnable接口的一个实例,它代表一个线程实例。开启线程的唯一方法就是通过Thread类的start()方法。start()方法将启动一个新线程,并执行run()方法。调用start()方法后,使得该线程变为可运行态(Runnable),具体什么时候执行多线程代码,是由操作系统决定的。

代码如下(示例):

public class ThreadTest extends Thread{
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"线程开始");
        Thread1 th = new Thread1();
        //启动新线程
        th.start();
        for(int i = 6; i >=0; i--){
            System.out.print(i + " ");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

class Thread1 extends Thread{
    @Override
    public void run() {
        System.out.print(Thread.currentThread().getName()+"线程开始 ");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.print(Thread.currentThread().getName()+"线程结束 ");
    }
}

运行结果:

main线程开始
6 Thread-0线程开始 5 4 3 2 Thread-0线程结束 1 0 

从上述测试可以看出,当我们在主线程中调用start()方法后,主线程线建一个线程,并且不会等待子线程的执行结果,而是会继续执行自己的逻辑。子线程的执行也不会收到主线程的影响。需要注意的是调用新线程的start()方法,而不要调用run()方法。只有start()方法才会新启动一个线程,run()方法会在原线程执行后续。


2.实现Runnable接口,并实现该接口的run()方法。

代码如下(示例):

public class RunnableTest {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"线程开始");
        //使用Runnable接口的线程对象创建方法
        Thread th = new Thread(new Thread2());
        Thread th2 = new Thread(new Thread2());
        //启动新线程
        th.start();
        for(int i = 6; i >=0; i--){
            System.out.print(i + " ");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


class Thread2 implements Runnable{

    @Override
    public void run() {
        System.out.print(Thread.currentThread().getName()+"线程开始 ");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.print(Thread.currentThread().getName()+"线程结束 ");
    }
}

运行结果:

main线程开始
6 Thread-0线程开始 5 4 3 2 Thread-0线程结束 1 0 

测试结果与继承Thread方法的效果一样。继承Thread类和实现Runnable接口得到一样的结果,但是我们更推荐使用实现Runnable接口来创建新线程。实现Runnable接口,我们会得到一个任务对象,再将任务对象装载到线程对象中,这样的代码更明确,实现任务与线程的解耦合,我们可以在定义一个任务后,将它装载到多个任务中。
代码如下(示例):

public class RunnableTest {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"线程开始");
        //使用Runnable接口的线程对象创建方法
        Thread th = new Thread(new Thread2());
        //重复装载同一个任务
        Thread th2 = new Thread(new Thread2());
        //启动新线程
        th.start();
        for(int i = 6; i >=0; i--){
            System.out.print(i + " ");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        th2.start();
    }
}

运行结果:

main线程开始
6 Thread-0线程开始 5 Thread-0线程结束 4 3 2 1 0 Thread-1线程开始 Thread-1线程结束  

3.实现Callable接口,重写call()方法。

在某些场景下主线程触发子线程后需要获得子线程的执行结果,这时候基于Runnable接口的实现方法就不能满足要求来,而是要基于Callable接口方法。
在Callable接口中,任务需要写在call方法中,call方法提供一个Object类型的放回置写入线程的执行结果。主线程可以获取这个返回值。不过Callable接口的实例不能直接装载到线程对象中执行,而需要使用FutureTask类进行包装。FutureTask 类是-一个包装器,它同时实现了Future 接口和Runnable接口。Future接口中定义了查询任务是否完成、取消任务、获得任务结果等操作方法。
代码如下(示例)

public class CallableTest {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"线程开始");
        //使用FutureTask包装任务
        FutureTask futureTask = new FutureTask(new Thread3());
        //装载任务到线程对象
        Thread th = new Thread(futureTask);
        //启动新线程
        th.start();
        for(int i = 6; i >=0; i--){
            System.out.print(i + " ");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try {
            //输出返回值
            System.out.println("return value from sub thread " + futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
class Thread3 implements Callable {

    @Override
    public Object call() throws Exception {
        System.out.print(Thread.currentThread().getName()+"线程开始 ");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.print(Thread.currentThread().getName()+"线程结束 ");

        return "result from" + Thread.currentThread().getName();
    }

}

运行结果:

6 Thread-0线程开始 5 4 Thread-0线程结束 3 2 1 0 return value from sub threadresult from Thread-0  

基于Callable接口实现多线程时,任务定义如上所示。它只是将Runnable接口改成了Callable接口,将run方法改成了call 方法,对任务对象使用FutureTask类进行包装,可以使用FutureTask所提供的方法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值