Java并发编程之线程创建和启动(Thread、Runnable、Callable和Future)

这一系列的文章暂不涉及Java多线程开发中的底层原理以及JMM、JVM部分的解析(将另文总结),主要关注实际编码中Java并发编程的核心知识点和应知应会部分。

说在前面,Java并发编程的实质,是线程对象调用start方法启动多线程,而线程对象则必须是Thread类或其子类实现。Runnable和Callable的作用类似于Comparable、Serializable,是用于被并发的类实现的接口,从而使得Thread类可以在初始化时传入这个被并发的类。此是大前提。本文从多线程实现和启动出发,对这些类或接口予以说明。

Thread

通过Thread的子类创建多线程的步骤如下:

1. 创建Thread的子类,并重写run()方法,该方法即为线程执行体。

2. 创建Thread子类的对象,即为线程对象。

3. 调用线程对象的start()方法启动线程。

 1 public class TestThread extends Thread{
 2
 3     public TestThread(String name) {
 4         setName(name);
 5     }
 6     @Override
 7     public void run() {
 8         while(!interrupted())
 9             System.out.println(getName() + "线程执行了");
10     }
11     public static void main(String[] args) {
12
13         TestThread t1 = new TestThread("first");
14         TestThread t2 = new TestThread("second");
15         //setDaemon()设置线程为守护线程
16 //        t1.setDaemon(true);
17 //        t2.setDaemon(true);
18         t1.start();
19         t2.start();
20         t1.interrupt();
21     }
22 }

Runnable

需要并发执行的类,可以通过实现Runnable接口,作为Thread的Target来创建线程对象。

 1 public class TestRunnable implements Runnable{
 2
 3     @Override
 4     public void run() {
 5         while(true) {
 6             System.out.println("thread running...");
 7             try {
 8                 Thread.sleep(1000);
 9             } catch (InterruptedException e) {
10                 e.printStackTrace();
11             }
12         }
13     }
14
15     public static void main(String[] args) {
16         //传入TestRunnable对象作为Target, 开启线程
17         Thread t = new Thread(new TestRunnable());
18         t.start();
19         //采用匿名内部类的方式创建和启动线程
20         new Thread() {
21             @Override
22             public void run() {
23                 System.out.println("Thread的匿名内部类");
24             }
25         }.start();
26         //父类采用匿名实现Runnable接口, 并由子类继承
27         new Thread(new Runnable() {
28
29             @Override
30             public void run() {
31                 System.out.println("父类的线程");
32             }
33         }) {
34             @Override
35             public void run() {
36                 System.out.println("子类的线程");
37             }
38         }.start();
39     }
40 }

Callable和Future

Java5开始提供了Callable接口,用于现有多线程开发的强力补充。Callable接口提供一个call()方法来构造线程执行体。

1. call()方法可以有返回值

2. call()方法可以声明抛出异常

因此Callable接口没有继承Runnable接口,不能直接作为Thread类的Target来构造线程对象,所以Java5提供了Future接口来代表call方法的返回值。

Future提供了FutureTask实现类,该实现类实现了Future接口和Runnable接口,像桥梁一样把线程执行体和线程对象连接了起来。

Future接口提供了若干公共方法来操作Callable任务:

boolean cancel(boolean mayInterruptIfRunning): 试图取消Future里关联的Callable任务

V get():返回Callable任务里call方法的返回值。调用该方法会导致阻塞,必须等子线程完成后才得到返回值

V get(long timeout, TimeUnit unit):最多阻塞timeout和unit指定的时间,超时将抛出TimeoutException异常

boolean isCancelled():Callable任务正常完成前被取消,则返回true

boolean isDone():Callable任务已完成,则返回true

创建并启动有返回值的线程步骤如下:

1. 创建Callable接口的实现类,并实现call方法作为线程执行体,再创建类的实例。Java8中可通过Lambda表达式进行。

2. 使用FutureTask类来包装Callable实现类的对象

3. 使用FutureTask作为Thread对象的target

4. 使用FutureTask对象的get方法获取子线程执行后的返回值

 1 public class TestCallable implements Callable<Integer>{
 2     //实现Callable并重写call方法作为线程执行体, 并设置返回值1
 3     @Override
 4     public Integer call() throws Exception {
 5         System.out.println("Thread is running...");
 6         Thread.sleep(3000);
 7         return 1;
 8     }
 9
10     public static void main(String[] args) throws InterruptedException, ExecutionException {
11         //创建Callable实现类的对象
12         TestCallable tc = new TestCallable();
13         //创建FutureTask类的对象
14         FutureTask<Integer> task = new FutureTask<>(tc);
15         //把FutureTask实现类对象作为target,通过Thread类对象启动线程
16         new Thread(task).start();
17         System.out.println("do something else...");
18         //通过get方法获取返回值
19         Integer integer = task.get();
20         System.out.println("The thread running result is :" + integer);
21     }
22 }

总结一下,虽然继承Thread类的开发方式相对简单,但因为Java单继承的限制,一般建议通过实现Runnable或Callable接口来创建并启动多线程。

原文地址:https://www.cnblogs.com/leoliu168/p/9914344.html

时间: 2024-10-16 03:29:42

Java并发编程之线程创建和启动(Thread、Runnable、Callable和Future)的相关文章

Java并发编程:线程的创建

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd

【转】Java并发编程:如何创建线程?

一.Java中关于应用程序和进程相关的概念 在Java中,一个应用程序对应着一个JVM实例(也有地方称为JVM进程),一般来说名字默认是java.exe或者javaw.exe(windows下可以通过任务管理器查看).Java采用的是单线程编程模型,即在我们自己的程序中如果没有主动创建线程的话,只会创建一个线程,通常称为主线程.但是要注意,虽然只有一个线程来执行任务,不代表JVM中只有一个线程,JVM实例在创建的时候,同时会创建很多其它的线程(比如垃圾收集器线程). 由于Java采用的是单线程编

【转】Java并发编程:线程池的使用

Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间. 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务? 在Java中可以通过线程池来达到这样的效果.今天我们就来详细讲解一下Java的线程池,首先我们从最核心的ThreadPool

Java并发编程:线程池的使用(转)

Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间. 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务? 在Java中可以通过线程池来达到这样的效果.今天我们就来详细讲解一下Java的线程池,首先我们从最核心的ThreadPool

Java并发编程:线程的同步

.title { text-align: center } .todo { font-family: monospace; color: red } .done { color: green } .tag { background-color: #eee; font-family: monospace; padding: 2px; font-size: 80%; font-weight: normal } .timestamp { color: #bebebe } .timestamp-kwd

Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)

Java并发编程系列[未完]: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程:线程间的协作(wait/notify/sleep/yield/join) 一.线程的状态 Java中线程中状态可分为五种:New(新建状态),Runnable(就绪状态),Running(运行状态),Blocked(阻塞状态),Dead(死亡状态). New:新建状态,当线程创建完成时为新

JAVA并发编程3_线程同步之synchronized关键字

在上一篇博客里讲解了JAVA的线程的内存模型,见:JAVA并发编程2_线程安全&内存模型,接着上一篇提到的问题解决多线程共享资源的情况下的线程安全问题. 不安全线程分析 public class Test implements Runnable { private int i = 0; private int getNext() { return i++; } @Override public void run() { // synchronized while (true) { synchro

19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权.因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去.因此,一般情况下,当队列满时,会让生产者交出对

JAVA并发编程4_线程同步之volatile关键字

上一篇博客JAVA并发编程3_线程同步之synchronized关键字中讲解了JAVA中保证线程同步的关键字synchronized,其实JAVA里面还有个较弱的同步机制volatile.volatile关键字是JAVA中的轻量级的同步机制,用来将变量的更新操作同步到其他线程.从内存可见性的角度来说,写入volatile变量相当于退出同步代码块,读取volatile变量相当于进入同步代码块. 旧的内存模型:保证读写volatile都直接发生在main memory中. 在新的内存模型下(1.5)