Java线程的常见创建办法

Java中常见的线程创建实现方式有三种:继承Tread类、实现runnable接口、调用Callable和Future。接下来,以抢票线程为例,分析3种实现办法,并对比3种方法的不同点。

1 继承Thread类,创建线程类

//继承Thread类创建线程
//第一步:新建类,继承Tread
public class ThreadInitial extends Thread{
    private int ticket = 3;//票数
    //调用Tread的构造函数,修改线程名字,便于理解
    public ThreadInitial(String str) {
        super(str);
    }
    //第二步:将需要执行的代码写到run()
    public void run() {
        for (int i = 0; i < 10; i++)
            if(ticket > 0)
            System.out.println(this.getName() + ": ticket"  + ticket--);//getName()获取当前线程的名字
    }

    public static void  main(String[] args) {
        System.out.println("Main thread: " + Thread.currentThread().getName());
        //第三步:执行start()
        //第一个线程
        new ThreadInitial("抢票线程1").start();
        //第二个线程
        new ThreadInitial("抢票线程2").start();
    }
}
/*Output
//因为CPU轮换执行原因,每次结果不同
Main thread: main
抢票线程2: ticket3
抢票线程1: ticket3
抢票线程2: ticket2
抢票线程1: ticket2
抢票线程2: ticket1
抢票线程1: ticket1
 */

如上图,该方法的线程创建主要是3步:

Step1:新建类,继承Thread

Step2:实现该类的run()方法,将需要在线程中运行的代码写于其中

Step3:创建实例,并执行start()方法

使用该方法创建的线程,多个线程之间无法共用线程例的实例变量。从打印信息我们可以理解:相当于两个票务员(线程)都获取了相同工作任务(抢票次数count)。两个票务员工作任务相同,但不是同一件工作。所以各自分别抢票3次。

2 实现runnable()接口

//实现Runnable()创建线程
//第一步:新建类,实现Runnable
public class ThreadInitial implements Runnable{
    private int ticket = 3;//抢票次数,线程实程,多个线程共用
    //第二步:将需要执行的代码写到run()
    public void run() {
        for (int i = 0; i < 10; i++)
            if (ticket > 0)
                System.out.println(Thread.currentThread().getName() + ": ticket" + ticket--);
    }

    public static void  main(String[] args) {
        System.out.println("Main thread: " + Thread.currentThread().getName());
        //第三步:创建实例
        ThreadInitial threadInitial = new ThreadInitial();
        //第四步:创建线程,执行start()
        //第一个线程
        new Thread(threadInitial,"抢票线程1").start();
        //第二个线程
        new Thread(threadInitial,"抢票线程2").start();
    }
}
/*Output
Main thread: main
抢票线程2: ticket3
抢票线程1: ticket2
抢票线程2: ticket1
 */

 

该方法的步骤如下,可以实现资源共享。可以理解为:分配两个票务员一件事件,这件事情由两个票务员合作完成。

Step1:创建implements Runnable()的类
Step2:实现该类中的run()方法,写入要执行的代码
Step3:创建该类实例
Step4:利用Thread(Runnable,String name)创建线程,并执行start()

该方法可以实现多线程资源共享。对比方法1的代码及打印信息、方法2的代码及打印信息发现:ticket是共享资源。方法1种没有实现共享,而是分别执行,共打印6条票务信息;方法2中两个线程共享ticket,只打印3条票务信息。原因可以Thread(Runnable target, String name)解读。源码如下:

//Thread(Runnable target, String name)
public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }

//Thread类中的run方法
@Override
public void run() {
    if (target != null) {
            target.run();
    }
}

此时发现,方法2中,两个线程的target是一样的,所以会执行同一个target.run().

3 利用Callable、FutureTask创建线程

//利用Callable、FutureTask创建线程
//第一步:定义实现Callable接口的类
public class ThreadInitial implements Callable<Integer> {
    //第二步:重写call()方法,即为线程执行体
    int ticket = 3;//票务数
    public Integer call() throws Exception {
        for (int i = 0; i < 10; i++)
            if(ticket > 0)
            System.out.println(Thread.currentThread().getName() + ": ticket" + ticket--);
        return ticket;
    }

    public static void main(String[] args) {
        //第三步:获取实现callable接口的类的实例
        ThreadInitial threadInitial = new ThreadInitial();
        //第四步:使用FutureTask类包装实现的类
        // 该FutureTask对象封装了该Callable对象的call()方法的返回值
        FutureTask<Integer> threadSeed = new FutureTask<>(threadInitial);//封装
        System.out.println("Main thread: " + Thread.currentThread().getId());
        //第五步:创建线程,并捕获异常
        new Thread(threadSeed,"抢票线程1").start();
        new Thread(threadSeed,"抢票线程2").start();
        //可以使用try catch捕获异常
        //可以使用threadSeed.get()获取线程执行体Call()的返回值
    }

}
/*OutPut:
Main thread: 1
抢票线程1: ticket3
抢票线程1: ticket2
抢票线程1: ticket1
 */

该方法的主要步骤如下,另外注意下打印信息,抢票线程2并没有执行,具体原因我还没有细致分析(抱歉,以后有时间再分析):

Step1:创建新类,implements Callable接口
Step2:重写Call()方法,该方法可返回值,可扔出异常
Step3:利用新创建类的实例
Step4:利用FutureTask封装该实例,获取FutureTask对象,该对象实现了Runnable接口
Step5:利用Tread(Runnable target)创建线程,并启动start()

4 主要区别

法1:extends Thread类实现创建线程;  法2: implements Runnable类创建线程;  法3:implement Callable+FutureTask封装

第1:法1 每次创建不同的target,无法进行线程间资源共享;法2与法3可以实现线程间资源共享,因其每次的target相同;

第2:法2与法3实现接口后,还可以继承其他类,程序具备一定可拓展性;

第3:法2与法1的执行体run()没有返回值,且不能捕获异常。法3可以做到。

第4:从代码上看出,复杂度上 法3 > 法2 > 法1; 功能上 法3 >  法2 > 法1

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值