JAVA并发编程——创建线程的多种方式

文章详细介绍了Java中四种创建线程的方法:继承Thread类、实现Runnable接口、通过Callable和FutureTask以及使用线程池。强调了Callable和FutureTask能获取执行结果的特点,以及线程池在性能优化和资源管理上的优势。
摘要由CSDN通过智能技术生成

目录

继承Thread类创建线程

实现Runnable接口创建线程 

 通过Callable和FutureTask创建线程

通过线程池创建线程 

每日寄语


继承Thread类创建线程


public class MultiThreads {

    public static void main(String[] args) throws InterruptedException {
        System.out.println(Thread.currentThread().getName());

        System.out.println("继承Thread类创建线程");
        SubClassThread subClassThread = new SubClassThread();
        subClassThread.start();  
    }
}

class SubClassThread extends Thread {

    @Override
    public void run() {
        System.out.println(getName());
    }
}

输出结果:         SubClassThread是一个继承了Thread类的子类,继承Thread类,并重写其中的run方法。然后new 一个SubClassThread的对象,并调用其start方法,即可启动一个线程。之后就会运行run中的代码。

每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。通过调用Thread类的start()方法来启动一个线程。

在主线程中,调用了子线程的start()方法后,主线程无需等待子线程的执行,即可执行后续的代码。而子线程便会开始执行其run()方法。

当然,run()方法也是一个公有方法,在main函数中也可以直接调用这个方法,但是直接调用run()的话,主线程就需要等待其执行完,这种情况下,run()就是一个普通方法。

如果读者感兴趣的话,查看一下Thread的源码,就可以发现,他继承了一个接口,那就是java.lang.Runnable,其实,开发者在代码中也可以直接通过这个接口创建一个新的线程。

实现Runnable接口创建线程 

public class MultiThreads {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(Thread.currentThread().getName());


        System.out.println("实现Runnable接口创建线程");
        RunnableThread runnableThread = new RunnableThread();
        new Thread(runnableThread).start();

      }
}

class RunnableThread implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

输出结果:

         通过实现接口,同样覆盖run()就可以创建一个新的线程了。

        我们都知道,Java是不支持多继承的,所以,使用Runnbale接口的形式,就可以避免要多继承 。比如有一个类A,已经继承了类B,就无法再继承Thread类了,这时候要想实现多线程,就需要使用Runnable接口了。

除此之外,两者之间几乎无差别。

但是,这两种创建线程的方式,其实是有一个缺点的,那就是:在执行完任务之后无法获取执行结果。

如果我们希望再主线程中得到子线程的执行结果的话,就需要用到Callable和FutureTask

 通过Callable和FutureTask创建线程

自从Java 1.5开始,提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。

public class MultiThreads {
    public static void main(String[] args) throws InterruptedException {
        CallableThread callableThread = new CallableThread();
        FutureTask futureTask = new FutureTask<>(callableThread);
        new Thread(futureTask).start();
        System.out.println(futureTask.get());
}

class CallableThread implements Callable {
    @Override
    public Object call() throws Exception {
        System.out.println(Thread.currentThread().getName());
        return "Hollis";
    }

}

输出结果:

main
通过Callable和FutureTask创建线程
Thread-2
Hollis

        Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法call(),和Runnable接口中的run()方法不同的是,call()方法有返回值。

        以上代码中,我们在CallableThread的call方法中返回字符串"Hollis",在主线程是可以获取到的。

        FutureTask可用于异步获取执行结果或取消执行任务的场景。通过传入Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。

        另外,FutureTask还可以确保即使调用了多次run方法,它都只会执行一次Runnable或者Callable任务,或者通过cancel取消FutureTask的执行等。

        值得注意的是,futureTask.get()会阻塞主线程,一直等子线程执行完并返回后才能继续执行主线程后面的代码。

        一般,在Callable执行完之前的这段时间,主线程可以先去做一些其他的事情,事情都做完之后,再获取Callable的返回结果。可以通过isDone()来判断子线程是否执行完。

以上代码改造下就是如下内容:

public class MultiThreads {
    public static void main(String[] args) throws InterruptedException {
        CallableThread callableThread = new CallableThread();
        FutureTask futureTask = new FutureTask<>(callableThread);
        new Thread(futureTask).start();

        System.out.println("主线程先做其他重要的事情");
        if(!futureTask.isDone()){
            // 继续做其他事儿
        }
        System.out.println(future.get()); // 可能会阻塞等待结果
}

        一般,我们会把Callable放到线程池中,然后让线程池去执行Callable中的代码。关于线程池前面介绍过了,是一种避免重复创建线程的开销的技术手段,线程池也可以用来创建线程。

通过线程池创建线程 

        Java中提供了对线程池的支持,有很多种方式。Jdk提供给外部的接口也很简单。直接调用ThreadPoolExecutor构造一个就可以了:

public class MultiThreads {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        System.out.println(Thread.currentThread().getName());
        System.out.println("通过线程池创建线程");
        ExecutorService executorService = new ThreadPoolExecutor(1, 1, 60L, TimeUnit.SECONDS,
            new ArrayBlockingQueue<Runnable>(10));
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        });
    }
}

输出结果:

 所谓线程池本质是一个hashSet。多余的任务会放在阻塞队列中。

        线程池的创建方式其实也有很多,也可以通过Executors静态工厂构建,但一般不建议。建议使用线程池来创建线程,并且建议使用带有ThreadFactory参数的ThreadPoolExecutor(需要依赖guava)构造方法设置线程名字,具体原因我们在后面的章节中在详细介绍。


每日寄语

        获得真正自由的方法是要学会自我控制。如果情绪总是处于失控状态,就会被感情牵着鼻子走,丧失自由。所以那些精神自由,保持独立思考的人也正是擅长于控制自己情绪的人。——尼采

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值