Java开启线程的4种方式

Posted zhujm320

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java开启线程的4种方式相关的知识,希望对你有一定的参考价值。

介绍

多线程是java中的一个重要的知识点,且在业务中经常使用,还是有必要掌握一下它。本文简要介绍一下线程的4种启动方式, 继承Thread,实现Runnable接口,实现Callable接口,通过线程池来启动线程。以上4种形式的线程启动方式,最终都是通过Thread.start()方法来真正启动线程。

 

继承Thread

通过继承java线程类Thread,并覆写run方法来实现线程。如下:

package com.stx.demo;

/**
 * 线程使用方式1
 * 直接继承Thread,并覆写run方法
 */
public class ThreadUse1 extends Thread{

    public ThreadUse1(String name) {
        super(name);
    }

    public static void main(String[] args) {
        System.out.println("---------------->");
        ThreadUse1 thread = new ThreadUse1("线程使用方式1");
        //启动线程
        thread.start();
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 正在执行...");
    }
}

 执行结果

---------------->
线程使用方式1 正在执行...

实现Runnable接口

通过实现Runnable接口,并将Runnable引用传递给Thread,Thread在执行时,run方法会调用Runnable.run方法。Thread的run方法如下:

@Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

这个target是我们传递进去的runnable

通过Runnable可以将线程业务逻辑和线程的执行代码分开,代码上条理比较清楚

package com.stx.demo;

/**
 * 线程使用方式2
 * 使用runnable接口,线程执行的业务逻辑放在runnable中,线程只负责调度
 */
public class ThreadUse2 implements Runnable{

    public static void main(String[] args) {
        System.out.println("---------------->");
        Runnable runnable = new ThreadUse2();
        Thread thread = new Thread(runnable, "线程使用方式2");
        thread.start();
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 正在执行...");
    }
}

执行结果

---------------->
线程使用方式2 正在执行...

实现Callable接口

实现Callable接口并配合FutureTask实现线程的阻塞调用,并将结果返回。和其他方法不同的是它实现了线程阻塞调用。代码如下:

package com.stx.demo;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;


/**
 * 线程使用方式3
 * 采用Callable接口方式,和runnable的不同就是阻塞调用线程,
 * 线程执行完毕后,拿到执行结果
 *
 */
public class ThreadUse3 implements Callable<String> {

    public static void main(String[] args) {

        long startTime = System.currentTimeMillis();
        ThreadUse3 threadUse3 = new ThreadUse3();
        FutureTask<String> futureTask =new FutureTask<>(threadUse3);
        Thread thread = new Thread(futureTask);
        thread.start();
        try {
            String result = futureTask.get();
            long cost = System.currentTimeMillis() - startTime;
            System.out.println("result: " + result + " cost: " + cost + "ms");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public String call() throws Exception {
        System.out.println(Thread.currentThread().getName() + "正在执行,将在1000ms后返回结果...");
        Thread.sleep(1000);
        return "返回结果";
    }
}

 执行结果

Thread-0正在执行,将在1000ms后返回结果...
result: 返回结果 cost: 1003ms

通过线程池启动线程

由于新开线程需要较大的系统开销,对于那些使用频繁且执行时间短的异步任务,如果每次都通过开线程来进行处理,势必增加在线程的创建和销毁上的系统开销。为了解决这种问题,启动线程池专门来解决这种频繁且执行时间短的任务,线程池里面常驻一定数量的线程,有异步任务过来时,就分配一个线程,让其去执行;没有任务则休眠;这样避免了频繁创建和销毁线程的系统开销。

使用例子如下:

package com.stx.demo;

/**
 * 线程池测试线程
 */
public class ThreadPoolRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 开始运行...");
        int delayTime = (int)(1000 * Math.random());
        System.out.println(Thread.currentThread().getName() + "执行时间: " + delayTime);
        try {
            Thread.sleep(delayTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 结束运行...");
    }
}
package com.stx.demo;

import java.util.concurrent.*;

/**
 * 线程使用方式4
 * 采用线程池启动线程
 */
public class ThreadUse4 implements Runnable{

    private LinkedBlockingQueue<Runnable> runnablesQueue;
    private ThreadPoolExecutor threadPoolExecutor;


    public ThreadUse4(){
        runnablesQueue = new LinkedBlockingQueue<>();
        int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
        int maximumPoolSize = 2*Runtime.getRuntime().availableProcessors() + 1;
        long keepAliveTime = 1000;
        TimeUnit unit = TimeUnit.MILLISECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(maximumPoolSize);
        RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                runnablesQueue.add(r);
            }
        };
        System.out.println("corePoolSize: " + corePoolSize + " , maximumPoolSize: " + maximumPoolSize);
        threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, rejectedExecutionHandler);
        //启动线程池核心线程(既ThreadUse4,然后通过run启动其他线程)
        threadPoolExecutor.execute(this);
    }


    public static void main(String[] args) {
        ThreadUse4 threadUse4 = new ThreadUse4();
        //往线程池中添加5个任务
        for (int i = 0; i < 5; i ++) {
            ThreadPoolRunnable task = new ThreadPoolRunnable();
            threadUse4.addTask(task);
        }
    }
    
    @Override
    public void run() {
        while (true) {
            try {
                //从队列中获取runnable放到线程池中去执行
                Runnable runnable = runnablesQueue.take();
                threadPoolExecutor.execute(runnable);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 添加任务到队列中
     */
    public void addTask(Runnable runnable){
        runnablesQueue.add(runnable);
    }
}

 执行结果:

corePoolSize: 13 , maximumPoolSize: 25
pool-1-thread-2 开始运行...
pool-1-thread-4 开始运行...
pool-1-thread-5 开始运行...
pool-1-thread-2执行时间: 400
pool-1-thread-5执行时间: 716
pool-1-thread-3 开始运行...
pool-1-thread-3执行时间: 425
pool-1-thread-4执行时间: 668
pool-1-thread-6 开始运行...
pool-1-thread-6执行时间: 969
pool-1-thread-2 结束运行...
pool-1-thread-3 结束运行...
pool-1-thread-4 结束运行...
pool-1-thread-5 结束运行...
pool-1-thread-6 结束运行...

小结:

以上4种启动线程的方式,本质上都是通过Thread类来启动线程,根据使用方式和场景的不同而衍生了4种使用方式。继承Thread,实现Runnable和使用线程池属于异步执行,需要回送结果,需要通过接口回调; 实现Callable接口来实现线程,则是阻塞调用,调用完毕就可以获取到结果。

 

 

以上是关于Java开启线程的4种方式的主要内容,如果未能解决你的问题,请参考以下文章

Java开启线程的4种方式

Java开启线程的4种方式

java多线程开启的三种方式

java多线程的3种实现方式

Java多线程-线程创建的3种方式

Java线程Thread使用匿名内部类创建的两种方式