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种方式的主要内容,如果未能解决你的问题,请参考以下文章