Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用四种方式来创建线程:

  • 继承Thread创建线程
  • 实现Runnable接口创建线程
  • 实现callable接口实现线程
  • 使用线程池Executor创建线程

1.继承Thread实现线程

  我们先来看一下Thread的源码,它是一个类,同样也实现了Runnable接口

public
class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
} private volatile String name;
private int priority;
private Thread threadQ;
private long eetop; /* Whether or not to single_step this thread. */
private boolean single_step; /* Whether or not the thread is a daemon thread. */
private boolean daemon = false; /* JVM state */
private boolean stillborn = false; /* What will be run. */
private Runnable target; /* The group of this thread */
private ThreadGroup group; /* The context ClassLoader for this thread */
private ClassLoader contextClassLoader; /* The inherited AccessControlContext of this thread */
private AccessControlContext inheritedAccessControlContext; /* For autonumbering anonymous threads. */
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
} /* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null; /*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; /*
* The requested stack size for this thread, or 0 if the creator did
* not specify a stack size. It is up to the VM to do whatever it
* likes with this number; some VMs will ignore it.
*/
private long stackSize; /*
* JVM-private state that persists after native thread termination.
*/
private long nativeParkEventPointer; /*
* Thread ID
*/
private long tid; /* For generating thread ID */
private static long threadSeqNumber; /* Java thread status for tools,
* initialized to indicate thread 'not yet started'
*/ private volatile int threadStatus = 0; //...... }
通过继承Thread类来创建并启动多线程的一般步骤如下
  • 1】d定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。
  • 2】创建Thread子类的实例,也就是创建了线程对象
  • 3】启动线程,即调用线程的start()方法

代码示例:

public class  ThreadTest {

    public static void main(String[] args) {
new MyThread().start();
} static class MyThread extends Thread {//继承Thread
public void run() {
System.out.println("我是继承Thread类!! ");
}
} }

2.实现Runnable接口创建线程

  我们来看一下Runnable的源码,它是一个接口:

@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。

通过实现Runnable接口创建并启动线程一般步骤如下:
  • 1】定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体
  • 2】创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
  • 3】第三部依然是通过调用线程对象的start()方法来启动线程

代码示例:

public class RunnableTest {

    public static void main(String[] args) {
MyThread2 myThread=new MyThread2();
Thread thread = new Thread(myThread);
thread.start();
} static class MyThread2 implements Runnable {
@Override
public void run() {
System.out.println("我是实现Runnable接口!! ");
}
}
}

3.实现callable接口实现线程

  我们来看一下callable源码,它是一个接口:

@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}

它和Runnable接口不一样的是,call()方法提供了2个额外功能:

  • call()方法可以有返回值
  • all()方法可以声明抛出异常

java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。在Future接口里定义了几个公共方法来控制它关联的Callable任务。

那么怎么使用Callable呢?一般情况下是配合ExecutorService来使用的,在ExecutorService接口中声明了若干个submit方法的重载版本:

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);

第一个submit方法里面的参数类型就是Callable。

暂时只需要知道Callable一般是和ExecutorService配合来使用的,具体的使用方法讲在后面讲述。

一般情况下我们使用第一个submit方法和第三个submit方法,第二个submit方法很少使用。

3.1 Future

我们来看一下Future的源码,它是一个接口:

public interface Future<V> {

    /**
* Attempts to cancel execution of this task. This attempt will
* fail if the task has already completed, has already been cancelled,
* or could not be cancelled for some other reason. If successful,
* and this task has not started when {@code cancel} is called,
* this task should never run. If the task has already started,
* then the {@code mayInterruptIfRunning} parameter determines
* whether the thread executing this task should be interrupted in
* an attempt to stop the task.
*
* <p>After this method returns, subsequent calls to {@link #isDone} will
* always return {@code true}. Subsequent calls to {@link #isCancelled}
* will always return {@code true} if this method returned {@code true}.
*
* @param mayInterruptIfRunning {@code true} if the thread executing this
* task should be interrupted; otherwise, in-progress tasks are allowed
* to complete
* @return {@code false} if the task could not be cancelled,
* typically because it has already completed normally;
* {@code true} otherwise
*/
boolean cancel(boolean mayInterruptIfRunning); /**
* Returns {@code true} if this task was cancelled before it completed
* normally.
*
* @return {@code true} if this task was cancelled before it completed
*/
boolean isCancelled(); /**
* Returns {@code true} if this task completed.
*
* Completion may be due to normal termination, an exception, or
* cancellation -- in all of these cases, this method will return
* {@code true}.
*
* @return {@code true} if this task completed
*/
boolean isDone(); /**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
*/
V get() throws InterruptedException, ExecutionException; /**
* Waits if necessary for at most the given time for the computation
* to complete, and then retrieves its result, if available.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
* @return the computed result
* @throws CancellationException if the computation was cancelled
* @throws ExecutionException if the computation threw an
* exception
* @throws InterruptedException if the current thread was interrupted
* while waiting
* @throws TimeoutException if the wait timed out
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}

我们来看一下它的各个方法:

  • boolean cancel(boolean mayInterruptIfRunning):用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
  • boolean isCancelled():如果在Callable任务正常完成前被取消,返回True
  • boolean isDone():若Callable任务完成,返回True
  • V get() throws InterruptedException, ExecutionException:返回Callable里call()方法的返回值,调用这个方法会导致程序阻塞,必须等到子线程结束后才会得到返回值
  • V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException:用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null

因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask

3.2 FutureTask

我们先来看一下FutureTask的实现:

public class FutureTask<V> implements RunnableFuture<V> {

FutureTask类实现了RunnableFuture接口,我们看一下RunnableFuture接口的实现:

public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}

可以看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

FutureTask提供了2个构造器:

public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}

事实上,FutureTask是Future接口的一个唯一实现类。

3.3 使用FutureTask对象作为Thread对象的target创建并启动线程

接下来我们看如何创建并启动有返回值的线程:

  • 1】创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
  • 2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
  • 3】使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
  • 4】调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

代码示例:

public class CallableAndFuture {
public static void main(String[] args) { Callable<Integer> call = new Callable<Integer>() {
public Integer call() throws Exception {
System.out.println("计算线程正在计算结果...");
Thread.sleep(3000);
return 1;
}
};
FutureTask<Integer> future = new FutureTask<>(call); new Thread(future,"有返回值的线程").start();//实质上还是以Callable对象来创建并启动线程 try {
System.out.println("子线程的返回值:" + future.get());//get()方法会阻塞,直到子线程执行结束才返回
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} }
}

3.4 使用executor创建线程

3.4.1.使用Callable+Future获取执行结果

代码示例:

public class CallableAndFuture {

    public static void main(String[] args) {
/** Executors提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。 */
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
Future<Integer> result = executor.submit(task); executor.shutdown(); try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
} System.out.println("主线程在执行任务"); try {
System.out.println("task运行结果"+result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} System.out.println("所有任务执行完毕");
} static class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子线程在进行计算");
Thread.sleep(3000);
int sum = 0;
for(int i=0;i<100;i++)
sum += i;
return sum;
}
}
}
3.4.2.使用Callable+FutureTask获取执行结果
public class CallableAndFuture2 {
public static void main(String[] args) {
//第一种方式
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
executor.submit(futureTask);
executor.shutdown(); //第二种方式,注意这种方式和第一种方式效果是类似的,只不过一个使用的是ExecutorService,一个使用的是Thread
/*Task task = new Task();
FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
Thread thread = new Thread(futureTask);
thread.start();*/ try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
} System.out.println("主线程在执行任务"); try {
System.out.println("task运行结果"+futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} System.out.println("所有任务执行完毕");
} static class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子线程在进行计算");
Thread.sleep(3000);
int sum = 0;
for(int i=0;i<100;i++)
sum += i;
return sum;
}
}
}

4.使用线程池Executor创建线程

4.1 Executor执行Runnable

public class Executor执行Runnable {

    /**
* 从结果中可以看出,pool-1-thread-1和pool-1-thread-2均被调用了两次,这是随机的,execute会首先在线程池中选择
* 一个已有空闲线程来执行任务,如果线程池中没有空闲线程,它便会创建一个新的线程来执行任务。
*/
public static void main(String[] args){
ExecutorService executorService = Executors.newCachedThreadPool();
// ExecutorService executorService = Executors.newFixedThreadPool(5);
// ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++){
executorService.execute(new TestRunnable());
System.out.println("************* a" + i + " *************");
}
executorService.shutdown();
}
} class TestRunnable implements Runnable {
public void run() {
System.out.println(Thread.currentThread().getName() + "线程被调用了。");
} }

执行结果:

************* a1 *************
************* a2 *************
pool-1-thread-2线程被调用了。
************* a3 *************
pool-1-thread-1线程被调用了。
pool-1-thread-2线程被调用了。
************* a4 *************
pool-1-thread-3线程被调用了。

4.2Executor执行Callable

public class Executor执行Callable {
/**
* 从结果中可以同样可以看出,submit也是首先选择空闲线程来执行任务,如果没有,才会创建新的线程来执行任务。
* 另外,需要注意:如果Future的返回尚未完成,则get()方法会阻塞等待,直到Future完成返回,可以通过
* 调用isDone()方法判断Future是否完成了返回。
*/
public static void main(String[] args){
ExecutorService executorService = Executors.newCachedThreadPool();
List<Future<String>> resultList = new ArrayList<Future<String>>(); //创建10个任务并执行
for (int i = 0; i < 10; i++){
//使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中
Future<String> future = executorService.submit(new TaskWithResult(i));
//将任务执行结果存储到List中
resultList.add(future);
} //遍历任务的结果
for (Future<String> fs : resultList){
try{
while(!fs.isDone());//Future返回如果没有完成,则一直循环等待,直到Future返回完成
System.out.println(fs.get()); //打印各个线程(任务)执行的结果
}catch(InterruptedException e){
e.printStackTrace();
}catch(ExecutionException e){
e.printStackTrace();
}finally{
//启动一次顺序关闭,执行以前提交的任务,但不接受新任务
executorService.shutdown();
}
}
}
} class TaskWithResult implements Callable<String> {
private int id; public TaskWithResult(int id){
this.id = id;
} /**
* 任务的具体过程,一旦任务传给ExecutorService的submit方法,
* 则该方法自动在一个线程上执行
*/
public String call() throws Exception {
System.out.println("call()方法被自动调用!!! " + Thread.currentThread().getName());
//该返回结果将被Future的get方法得到
return "call()方法被自动调用,任务返回的结果是:" + id + " " + Thread.currentThread().getName();
}
}

执行结果:

call()方法被自动调用!!!    pool-1-thread-1
call()方法被自动调用,任务返回的结果是:0 pool-1-thread-1
call()方法被自动调用!!! pool-1-thread-2
call()方法被自动调用,任务返回的结果是:1 pool-1-thread-2
call()方法被自动调用!!! pool-1-thread-3
call()方法被自动调用,任务返回的结果是:2 pool-1-thread-3
call()方法被自动调用!!! pool-1-thread-5
call()方法被自动调用!!! pool-1-thread-6
call()方法被自动调用!!! pool-1-thread-7
call()方法被自动调用!!! pool-1-thread-9
call()方法被自动调用!!! pool-1-thread-4
call()方法被自动调用,任务返回的结果是:3 pool-1-thread-4
call()方法被自动调用,任务返回的结果是:4 pool-1-thread-5
call()方法被自动调用,任务返回的结果是:5 pool-1-thread-6
call()方法被自动调用,任务返回的结果是:6 pool-1-thread-7
call()方法被自动调用!!! pool-1-thread-8
call()方法被自动调用,任务返回的结果是:7 pool-1-thread-8
call()方法被自动调用,任务返回的结果是:8 pool-1-thread-9
call()方法被自动调用!!! pool-1-thread-10
call()方法被自动调用,任务返回的结果是:9 pool-1-thread-10

java多线程之创建线程的4种方式及Future的更多相关文章

  1. Java多线程之创建线程的三种方式比较

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6560057.html  一:继承Thread类创建线程 1:继承Thread类定义线程子类: 2:重写run( ...

  2. JAVA·多线程:创建线程的两种方式

    1.–扩展java.lang.Thread类 package multiThread; public class Thread02extThread { public static void main ...

  3. 1.java多线程_实现线程的两种方式

    1.java多线程基本知识 1.1.进程介绍 不管是我们开发的应用程序,还是我们运行的其他的应用程序,都需要先把程序安装在本地的硬盘上.然后找到这个程序的启动文件, 启动程序的时候,其实是电脑把当前的 ...

  4. Java多线程——之一创建线程的四种方法

    1.实现Runnable接口,重载run(),无返回值 package thread; public class ThreadRunnable implements Runnable { public ...

  5. java的线程、创建线程的 3 种方式、静态代理模式、Lambda表达式简化线程

    0.介绍 线程:多个任务同时进行,看似多任务同时进行,但实际上一个时间点上我们大脑还是只在做一件事情.程序也是如此,除非多核cpu,不然一个cpu里,在一个时间点里还是只在做一件事,不过速度很快的切换 ...

  6. 0036 Java学习笔记-多线程-创建线程的三种方式

    创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...

  7. Java并发编程:Java创建线程的三种方式

    目录 引言 创建线程的三种方式 一.继承Thread类 二.实现Runnable接口 三.使用Callable和Future创建线程 三种方式的对比 引言 在日常开发工作中,多线程开发可以说是必备技能 ...

  8. java创建线程的三种方式及其对比

    第一种方法:继承Thread类,重写run()方法,run()方法代表线程要执行的任务.第二种方法:实现Runnable接口,重写run()方法,run()方法代表线程要执行的任务.第三种方法:实现c ...

  9. AJPFX总结java创建线程的三种方式及其对比

    Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务.因此把run()方法称为执行 ...

随机推荐

  1. Linux curl 命令详解

    命令概要 该命令设计用于在没有用户交互的情况下工作. curl 是一个工具,用于传输来自服务器或者到服务器的数据.「向服务器传输数据或者获取来自服务器的数据」 可支持的协议有(DICT.FILE.FT ...

  2. 源码分析--dubbo服务端暴露

    服务暴露的入口方法是 ServiceBean 的 onApplicationEvent.onApplicationEvent 是一个事件响应方法,该方法会在收到 Spring 上下文刷新事件后执行服务 ...

  3. 非常详细的Django使用Token(转)

    基于Token的身份验证 在实现登录功能的时候,正常的B/S应用都会使用cookie+session的方式来做身份验证,后台直接向cookie中写数据,但是由于移动端的存在,移动端是没有cookie机 ...

  4. .netcore consul实现服务注册与发现-集群部署

    一.Consul的集群介绍 Consul Agent有两种运行模式:Server和Client.这里的Server和Client只是Consul集群层面的区分,与搭建在Cluster之上的应用服务无关 ...

  5. 03 requests模块基础

    1. requests 模块简介 什么是requests 模块 requests模块是python中原生的基于网络请求的模块,功能强大,用法简洁高效.在爬虫领域中占据着半壁江山的地位.requests ...

  6. JavaScript数组方法大全(第二篇)

    数组方法大全(第二篇) 注意:如有错误欢迎指出,如有雷同纯属巧合,本博客参考书籍JavaScript权威指南,有兴趣的小伙伴可以去翻阅一下哦 forEach()方法 遍历数组,里面可以传递一个方法 v ...

  7. 一句道破所有的springmvc(面试必备)

    springmvc流程 : URL--------前端控制器DispatcherServlet---------HandlerMapping处理器映射器-------调用HandlerAdapter处 ...

  8. SpringIoC和SpringMVC的快速入门

    更多内容,欢迎关注微信公众号:全菜工程师小辉~ Spring的优势? 降低了组件之间的耦合性 ,实现了软件各层之间的解耦 可以使用容易提供的众多服务,如事务管理,消息服务等 容器提供单例模式支持 容器 ...

  9. TomatoLog-1.1.0实现ILoggerFactory

    TomatoLog TomatoLog 是一个基于 .NETCore 平台的产品. The TomatoLog 是一个中间件,包含客户端.服务端,非常容易使用和部署. 客户端实现了ILoggerFac ...

  10. Python学习 之三 Python基础&运算符

    第三章:Python基础 & 运算符 3.1 内容回顾 & 补充 计算机基础 编码 字符串: "中国" "Hello" 字 符: 中 e 字 节 ...