Java创建线程方法

4 篇文章 0 订阅

本文总结自《疯狂Java讲义》

一、继承Thread类

  1. 定义 Thread 类的子类,并重写该类的 run() 方法,该 run() 方法的方法体就代表了线程需要完成的任务。因此把 run() 方法称为线程执行体。
  2. 创建 Thread 子类的实例,即创建了线程对象。
  3. 调用线程对象的 start() 方法来启动该线程。

示例代码如下:

public class ThreadTest extends Thread{
    private int x;
    public void run(){
        for (; x < 10; x++) {
            System.out.println(getName() + " " + x);    //获取线程名字
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+ " " + i);    //获取当前线程的名字
            if(i == 5){
                new ThreadTest().start();
                new ThreadTest().start();
            }
        }
    }
}

输出结果:

main 0
main 1
main 2
main 3
main 4
main 5
main 6
main 7
main 8
main 9
Thread-0 0
Thread-0 1
Thread-0 2
Thread-0 3
Thread-1 0
Thread-0 4
Thread-1 1
Thread-1 2
Thread-1 3
Thread-1 4
Thread-1 5
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9
Thread-0 5
Thread-0 6
Thread-0 7
Thread-0 8
Thread-0 9

我们可以看到,main函数是最先执行完的,因为开辟一个线程后,该线程就会进入就绪状态,并不会直接进入运行状态,状态的切换由底层平台控制的,有一定的随机性。

如果main函数的循环次数再大一些,线程0和线程1就能在main函数执行完前开始执行。

二、实现Runnable接口

  1. 定义 Runnable 接口的实现类,并重写该接口的 run() 方法,该 run() 方法的方法体同样是该线程的线程执行体。
  2. 创建 Runnable 实现类的实例,并以此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。
  3. 调用线程对象的 start() 方法来启动线程

示例代码如下:

public class ThreadTest implements Runnable{
    private int x;
    public void run(){
        for (; x < 10; x++) {
            System.out.println(Thread.currentThread().getName() + " " + x);
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+ " " + i);
            if(i == 0){
                new Thread(new ThreadTest()).start();
                new Thread(new ThreadTest()).start();
            }
        }
    }
}

输出结果:

main 0
main 1
main 2
main 3
main 4
main 5
main 6
main 7
main 8
main 9
Thread-0 0
Thread-1 0
Thread-1 1
Thread-1 2
Thread-0 1
Thread-1 3
Thread-0 2
Thread-0 3
Thread-0 4
Thread-0 5
Thread-0 6
Thread-1 4
Thread-1 5
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9
Thread-0 7
Thread-0 8
Thread-0 9

区别主要在于,线程的创建是将实现了 Runnable 的类的实例作为参数,通过 new Thread(new threadTest())方法来创建,再调用线程的 start() 方法执行线程。

三、使用Callable和Future

  1. 创建 Callable 接口的实现类 ,并实现 call() 方法,该 call() 方法将作为线程执行体,且该 call() 方法有返回值,再创建 Callable 实现类的实例。
  2. 使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该Callable 对象的 call() 方法的返回值。
  3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
  4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

示例代码如下:

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadTest {
    public static void main(String[] args) {
        FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int i = 0;
                for (; i < 10; i++){
                    System.out.println(Thread.currentThread().getName() + " 的循环变量i的值:" + i);
                }
                return i;
            }
        });
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "的循环变量i的值:" + i);
            if (i == 5){
                new Thread(task, "有返回值的线程").start();
            }
        }
        try{
            System.out.println("子线程的返回值:" + task.get());
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

输出结果:

main的循环变量i的值:0
main的循环变量i的值:1
main的循环变量i的值:2
main的循环变量i的值:3
main的循环变量i的值:4
main的循环变量i的值:5
main的循环变量i的值:6
main的循环变量i的值:7
main的循环变量i的值:8
main的循环变量i的值:9
有返回值的线程 的循环变量i的值:0
有返回值的线程 的循环变量i的值:1
有返回值的线程 的循环变量i的值:2
有返回值的线程 的循环变量i的值:3
有返回值的线程 的循环变量i的值:4
有返回值的线程 的循环变量i的值:5
有返回值的线程 的循环变量i的值:6
有返回值的线程 的循环变量i的值:7
有返回值的线程 的循环变量i的值:8
有返回值的线程 的循环变量i的值:9
子线程的返回值:10

Callable 接口看起来就像是 Runnable 接口的增强版,call() 作为线程执行体,可以有返回值,还可以抛出异常。

Callable 接口没有实现 Runnable 接口,不能作为 Thread 的 target ,无法直接创建线程对象。

Callable 接口有泛型限制,返回值类型要与泛型类型一致。

Future 接口代表 Callable 接口里 call() 方法的返回值,并提供了一个实现类 FutureTask ,该实现类也实现了 Runnable 接口,因此可以将 FutureTask 与 Callable 关联起来,实现多线程。

三种方法的对比

  • 继承 Thread 的方法已经继承了 Thread 类,无法再继承别的类,而 Runnable 和 Callable 可以继承别的类。
  • Runnable 和 Callable 可以多个线程共享同一个 target 对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码、数据分开,形成清晰的模型,较好地体现了面向对象的思想。
  • Callable 类的线程可以经过 Future 包装后获取返回值。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值