多线程

一、开启线程的3种方式

1.继承Thread类
public class MyThread extends Thread{
    public void run(){
        //线程运行主体
    }
}

class MainDemo{
    public static void main(Stirng args[]){
        Mythread mythread=new MyThread();//创建一个线程实例,此时就已经为改线程分配好了内存空间。
        mythread.start();//启动改线程,启动之后,会自动调用run()方法内容。
    }
}

继承Thread类,需要重写run()方法。

缺点,由于Java是单继承的,继承Thread类后,实现类无法再继承其他类。

并且通过继承方式实现,线程数据无法实现数据共享。

2.实现Runnable接口
public class MyThread implements Runnable{
    public void run(){
        //线程运行主体
    }
}

class MainDemo{
    public static void main(Stirng args[]){
        Mythread my=new MyThread();//创建一个线程实现类实例
        
        //创建一个线程实例(实现Runnable接口的类实例作为参数),此时就已经为改线程分配好了内存空间。
        Thread thread1=new Thread(my);
        //再创建一个线程实例(实现Runnable接口的类实例作为参数)。
        Thread thread2=new Thread(my);
        
        thread1.start();//启动线程1,启动之后,会自动调用run()方法内容。
        thread2.start();//启动线程2,
    }
}

实现Runnable接口,也需要重写run()方法

优点相比较继承Thread的方式,实现Runnable方式更加灵活。可以实现多个线程,跑同一个run()方法。以达到多个线程共享一份数据的目的。

3.实现Callable接口
public class MyThread implements Callable<String>{
    public String call(){
        //线程运行主体
    }
}

class MainDemo{
    public static void main(Stirng args[]){
        //创建一个线程实现类实例
        Mythread my=new MyThread();
        
        //创建一个FutureTask()对象实例,参数为实现了Callable接口类的实例。
        FutureTask<String> fu=new FutureTask<String>(my);
        fu
        //创建一个线程实例(FutureTask类实例作为参数),此时就已经为改线程分配好了内存空间。
        Thread thread=new Thread(fu);
        
        //启动线程
        thread.start();
        
        //线程启动运行后,可以通过FutureTask类的get方法,取得该线程call()方法的运行返回值。
        //call()的本质也就是run()方法
        String result=fu.get();
    }
}

实现Callable接口,需要重写call()方法。并且实现时需要设置泛型,这个泛型也就是call()方法的返回值类型。

与其他两种方式最大的不同在于可以给线程运行的主体(call()方法)添加一个返回值,并且可以通过一个get()方法取得这个值。get()方法由FutureTask类提供。

二、从源码角度简单分析这三种实现方式

1.Runnable接口原理

Runnable接口,该接口只有一个方法run()

通过实现Runnable接口的方式,启动线程时,还是需要一个Thread类的实例,因为只有Thread类才有与线程操作相关的方法。

Runnable实现类的实例作为参数传递给Thread实例,实际上就是将一个重写好的run()方法提供给了Thread实例去执行。

Thread类中,有一个Runnable类型的成员变量targetThread中的run()方法判断target不为空时,就会执行targetrun()方法。如果为空,不会有任何操作。

2.Thread类原理

Thread类,该类实现了Runnable接口,并重写了run()方法。

但是线程相关的许多操作都是再该类里面定义的。如start()sleep()等。

一个类继承了Thread类之后,会重写它的run()方法,该类调用Start()方法启动线程之后,线程会执行本类的run()方法。

3.Callable接口原理

Callable接口,该接口只有一个call()方法。作用与run()方法类似,都是一个线程的运行主体。

但线程的最终运行过程是:

1.实现了Runnable接口的FutureTask类中重写的run()方法种调用了Callable接口实现类的call()方法。

2.然后Thread类调用FutureTask类中的run()方法。这一步与第一种方式的原理类似。因为FutureTask类实现了Runnable接口。

4.总结

Java实现多线程从方式上来说,虽然有3种,但起本质其实都是Thread类,不管那种方式,最终都会回到Thread类。另外两种方式,只是在最本质的一种方式上的封装。给Java多线程机制提供了更多的可能。否则只有继承Thread这一种方式,未免太过于局限性。

对于Runnable接口,它就像一个生产集装箱的工厂,我们把一个线程要执行的方法体放在里面一个集装箱里面。

对于Thread类就像一个卡车。集装箱放上去,它只管带着集装箱跑就行了,当然如果其他人不提供集装箱,Thread这个卡车,自己也有一个集装箱,不过这个集装箱也是从Runnable工厂里出来的,但是它是空的,需要我们自己填满。

对于Callable方式,就相当于一个叫FutureTask的商人找了Runnable工厂定制了一种集装箱,这种集装箱上面有一个小口口,可以从集装箱里面拿东西出来。但是还是离不开Thread卡车。

上面说提到的工厂,绝不代表工厂设计模式。 只是我举例,与其他什么设计模式无关联,因为设计模式我完全还不懂。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值