Java多线程实现的4中方式

对于所有语言来说,多线程的编程是绝不可少的。同样的Java语言也包含了多线程的开发。首先,我们先来了解一下Java语言的多线程实现方式。

一、Java 多线程实现方式

java中实现多线程的方式有三种,接下来我将会逐个进行介绍。

1.继承Thread类

继承Thread类是Java中比较常见,也是很基础的一种实现Java多线程的方式。实现的方式也比较简单,只要将需要实现多线程的Java类继承java.lang.Thread类即可。

class MyThread extends Thread{

private String name;

public MyThread(String name){

this.name = name;

}

@Override

public void run() {

Thread.currentThread().setName(name);

System.out.println("I am Thread :" +name);

}

}

如上代码片段则是通过继承Thread类实现多线程的方式之一。那么多线程该如何调用呢?请接着阅读下边的代码。

public class threadLearn {

public static void main(String[] args)  {

//实例化继承了Thread的类

MyThread thread1 = new MyThread("Thread1");

//通过从Thread类中所继承的start()方法启动线程;

thread1.start();

}

}

运行上边的代码,可以得到结果:

到这里,一个简单的线程已经被启动了。下边我们接着介绍另外两种多线程的实现方式。

2.实现Runable接口

接下来请看这样一种场景,DogRobot是一个用来看门的Dog,现在需要多个Dog分别把守不同的位置,但DogRobot一定要继承Robot父类,此时应该如何实现多线程呢?

在这种场景下,可以通过实现Runable接口的方式来实现一个类的多线程。

我们知道,在Java中,类的继承是单一的,即一个子类仅可以继承一个父类(一个儿子只有一个爹,符合自然规律),但可以实现多个接口。那么,通过Runable接口来实现多线程的好处自然不言而喻。

接下来看一下实现方式:

public class threadLearn {

public static void main(String[] args)  {

//实例化继承了Thread的类

MyThread thread1 = new MyThread("Thread1");

//通过从Thread类中所继承的start()方法启动线程;

thread1.start();

/*

* 实现Runnable的类,需要将其放在一个Thread实例对象中,

* 由Thread实例对象进行线程的启动及控制。

*/

Thread threadDog = new Thread(new DogRobot("kiki"));

threadDog.start();

}

}

class DogRobot extends Robot implements Runnable{

private String name;

public DogRobot(String name){

super(name);

this.name = name;

}

@Override

public void run() {

Thread.currentThread().setName(name);

System.out.println("I am DogRobot :" +name);

}

}

class Robot {

private String Name;

public Robot(String name)

{

this.Name = name;

}

}

下边接着讲解第三种多线程的实现方式。

3. 使用Executor框架实现多线程

在介绍Excutor实现多线程之前,我们先来学习另外一种多线程的实现方式,即继承Callable接口实现多线程的方式。

首先我们来看一下Callable的接口:

可以看到,Callable的接口只有一个方法,那么我们在实现这个接口的时候也仅需要实现这个方法即可。

/*

* Callable接口实现多线程Demo

*/

class  MyCallable<V> implements Callable<V>

{

@Override

public V call() throws Exception {

// TODO Auto-generated method stub

System.out.println("I am Callable thread : "+Thread.currentThread().getName());

return null;

}

}

如何调用这个线程,使其执行任务呢?那么,是需要通过FutureTask<V>这个类的实例去调度,我们首先来看一下FutureTask类的结构:

public class FutureTask<V> implements RunnableFuture<V>

这是FutureTask的实现方式,我们可以看到FutrueTask是实现了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接口的一个接口,至于Future接口是什么,我们后续会讲到。

如果您看到了Runnable接口,那么我想应该已经明了了,实现了Runnable接口的类,可以通过Thread实例对象来驱动,从而运行一个独立的线程。这是我们前边所讲到的。

到这里,还有一个问题,FutureTask和Callable接口有什么关系呢?

那么我们接着看FutureTask的构造方法:

/**

* Creates a {@code FutureTask} that will, upon running, execute the

* given {@code Callable}.

*

* @param  callable the callable task

* @throws NullPointerException if the callable is null

*/

public FutureTask(Callable<V> callable) {

if (callable == null)

throw new NullPointerException();

this.callable = callable;

this.state = NEW;       // ensure visibility of callable

}

可以看到,FutureTask的构造方法之一所需要的参数,就是Callable的实例对象。为什么说之一呢,因为还有一个构造方法,是通过Runnable接口实现的,如何实现的呢:

public FutureTask(Runnable runnable, V result) {

this.callable = Executors.callable(runnable, result);

this.state = NEW;       // ensure visibility of callable

}

其实我们继续跟入进去可以发现,其实这个方法本质上还是生成了一个Callable的对象,最后赋予自己内部的this.callable;感兴趣的同学可以自己试一试这种方式。

回到我们的话题,继续实现调用:

public static void main(String[] args)  {

/*

* 使用Callable来创建线程

*/

Callable <Integer> aCallable = new MyCallable<Integer>();

FutureTask<Integer> aTask = new FutureTask<Integer>(aCallable);

Thread aThread = new Thread(aTask);

aThread.start();

}

我们运行一下程序,可以看到,运行的结果如下图所示:

到这里,一个通过Callable实现的接口便是成功了。

那么这里会有一个问题,按照这样的实现方式,那和Runnable接口有什么区别???

其实我们应该注意到了,Callable接口里的call方法,是一个有返回值的方法;

且FutureTask类的实现方式中,针对Runnable的实现方式,也是携带有一个参数result,由result和Runnable实例去合并成一个Callable的实例。

小本本拿出来划重点了。

实现了Callable接口的线程,是具有返回值的。而对于一些对线程要求有返回值的场景,是非常适用的。

接下来,我们就看一下,如何获通过Callable接口获取线程的返回值。

获取Callable接口的返回值,需要使用Future接口的实例化对象通过get的方式获取出来。

Mark -- 2018 04 24

今天接着学习多线程。昨天说到通过实现Callable接口实现多线程,今天我们来看如何通过Callable线程获取到线程的返回值。

首先我们先来认识几个接口及一个工厂类

ExecutorService 接口

public interface ExecutorService extends Executor

ExecutorService接口是实现线程池经常使用的接口。通常我们使用的线程池、定时任务线程池都是他的实现类。

Executors工厂类

public class Executors

Executors是一个工厂类,通过调用工厂类的方法,可以返回各种线程池的实现类。比如我们接下来要用到的:

public static ExecutorService newFixedThreadPool(int nThreads) {

return new ThreadPoolExecutor(nThreads, nThreads,

0L, TimeUnit.MILLISECONDS,

new LinkedBlockingQueue<Runnable>());

}

我们可以看到,返回值的类型是一个ExecutorService 而实际上返回的返回值,则是一个ThreadPoolExecutor ,我们接着跟进去看下ThreadPoolExecutor是什么:

public class ThreadPoolExecutor extends AbstractExecutorService

到这里,我们可以看到,ThreadPoolExecutor是一个继承了AbstractExecutorService 的实现类。

这个线程池类里定义了各种我们需要对线程池进行操作的方法。后续有时间我们再研究一下源码。

有了上述的这些基本了解,我们则可以进行线程池的创建:

int taskSize = 5;

ExecutorService  exPool = Executors.newFixedThreadPool(taskSize);

如上边这样的代码,我们就可以创建一个具有5个线程容量的线程池。

那么如何将Callabe接口添加到线程池中运行呢?

ExcutorService提供了一个方法供我们调用:

<T> Future<T> submit(Callable<T> task);

我们看到,这个submit方法也只是接口里定义的一个方法,那么他的实现方法是什么呢,我们代码里跟一下:

可以看到,有很多个实现的方法,那么,我们这里调用的是哪一个呢?

很显然,我们调用的是AbstractExecutorService中的实现方法。因为用来调用submit的实例对象实际上正是ThreadPoolExecutor,刚才我们也提到了,ThreadPoolExecutor是继承了AbstractExcutorService的。

那么结果应该相当的明显了吧。

/**

* @throws RejectedExecutionException {@inheritDoc}

* @throws NullPointerException       {@inheritDoc}

*/

public <T> Future<T> submit(Callable<T> task) {

if (task == null) throw new NullPointerException();

RunnableFuture<T> ftask = newTaskFor(task);

execute(ftask);

return ftask;

}

看到这里是不是觉得有一丝丝的熟悉,正如我们第三小节所提到的 RunnableFuture ,从本质上,我们还是返回了一个RunnableFuture类型的实例。因此,我们可以通过该实例获取到线程的返回值。

那么接下来,我们写一个小demo来实践一下,通过ExcutorService来加载Callable接口实现多线程,并且得到返回值。

首先,我们来写一个Callable接口的实现类

/*

* Callable接口实现多线程Demo

*/

class  MyCallable implements Callable<Object>

{

private int task_id;

MyCallable(int task_id)

{

this.task_id = task_id;

}

@Override

public Object call() throws Exception {

// TODO Auto-generated method stub

System.out.println("I am Callable thread : "+Thread.currentThread().getName());

Random rd = new Random();

int leng = rd.nextInt(9)+1;

int sum = 0 ;

for(int i = 0 ; i <= leng ; i++ )

{

Thread.sleep(1000);

sum += i;

}

System.out.println("I am Callable thread : "+Thread.currentThread().getName()

+"Thread name is : ["+this.task_id+"]"

+" I worked done at ["+new Date().getTime()+"]"

+" Random Num = "+leng

);

return "The task ["+this.task_id+"] get result :【 "+ sum +" 】";

}

}

这个线程会返回1-10以内的随机数的自增合。我们来通过线程池调度一下:

/*

*使用Excutor来执行线程,并获取返回值

*/

int taskSize = 5;

ExecutorService  exPool = Executors.newFixedThreadPool(taskSize);

//使用Future来获取线程的结果

List<Future> list = new ArrayList<Future>();

for (int i = 0; i < taskSize; i++) {

Callable c = new MyCallable(i);

// 执行任务并获取Future对象

Future f = exPool.submit(c);

list.add(f);

}

exPool.shutdown();

System.out.println("The time we shutDown The Executor Pool : 【"+new Date().getTime()+"】");

for (Future f : list) {

// 从Future对象上获取任务的返回值,并输出到控制台

try {

System.out.println(">>>" + f.get().toString());

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (ExecutionException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

我们创建了一个List用来存放每个线程的执行结果。

shutdown()方法并不是终止线程池,而是在执行shutdown()方法之后,线程池则不会再接收新加入的线程。

我们运行一下 ,下边的是运行的结果:

从这个结果上,我们可以看到,线程4抽到的随机数是1,那么他执行的时间是最短的,只sleep 1S, 其他的线程2/3/1分别sleep了3/4/7S,线程5Sleep了9S,有意思的事情发生了。

我们这条长长的输出实在线程执行完自己的计算之后输出的,还没有返回值,此时其他的几个线程的输出结果已经答应出来了,线程5在执行完毕之后,主线程才打印出了线程5的结果。

到这里,关于Java线程的创建就告一段落。随后我们将会继续深入学习Java线程以及线程池的知识。

原文地址:https://www.cnblogs.com/wowostudy/p/8947670.html

时间: 2024-10-13 23:07:55

Java多线程实现的4中方式的相关文章

java多线程之从任务中获取返回值

package wzh.test; import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; class TaskWithResult implements Callable<Strin

java多线程开启的三种方式

1.继承Thread类,新建一个当前类对象,并且运行其start()方法 1 package com.xiaostudy.thread; 2 3 /** 4 * @desc 第一种开启线程的方式 5 * @author xiaostudy 6 * 7 */ 8 public class Demo1_Thread extends Thread { 9 10 public void run() { 11 for (int i = 0; i < 10; i++) { 12 System.out.pri

JAVA多线程实现的三种方式

JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程. 当中前两种方式线程运行完后都没有返回值,仅仅有最后一种是带返回值的. 1.继承Thread类实现多线程 继承Thread类的方法虽然被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,而且,启动线程的唯一方法就是通过Thread类的start()实例方法.start(

【转】JAVA多线程实现的四种方式

原文地址:http://www.cnblogs.com/felixzh/p/6036074.html Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorService.Callable.Future实现有返回结果的多线程. 其中前两种方式线程执行完后都没有返回值,后两种是带返回值的. 1.继承Thread类创建线程Thread类本质上是实现了Runnable接口的一个实

JAVA多线程实现的四种方式

Java多线程实现方式主要有四种: 继承Thread类: 实现Runnable接口: 实现Callable接口通过FutureTask包装器来创建Thread线程: 使用接口ExecutorService.Callable.Future实现有返回结果的多线程. 其中前两种方式线程执行完后都没有返回值,后两种是带返回值的. 1.继承Thread类创建线程Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例.启动线程的唯一方法就是通过Thread类的start()实例方法.

AJPFX关于JAVA多线程实现的三种方式

JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的. 1.继承Thread类实现多线程继承Thread类的方法尽管被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法.start()方法

Java多线程的两种实现方式的区别以及深刻同步问题中的锁对象

首先我们知道创建线程有两种方式: 1.继承Thread类:2.实现Runnable接口. 但是这两这并非完全一样的.下面谈谈区别: 因为Java并不支持多继承的(接口是可以多继承接口的.不过一般我们不提),但支持多实现.当一个类继承了父类,就不能再继承Thread类,只能通过实现接口的形式创建线程. 继承Runnable接口更加符合面向对象的思想.线程分为两部分,一是线程对象,二是线程任务.继承Thread类,线程对象和线程任务(run方法内的代码)耦合在一起.一旦创建了Thread类的子类对象

Java多线程的两种实现方式:继承Thread类 &amp; 实现Runable接口

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! 创建和启动线程的两种传统方式: Java提供了线程类Thread来创建多线程的程序.其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread类或其子类的实例对象.每个Thread对象描述了一个单独的线程.要产生一个线程,有两种方法: ◆需要从Java.lang.Thread类派生一个新的线程类,重载它的run()方法: ◆实现Runnalbe接口,重载Runnalbe接口中的run()方法.

java实现线程的3中方式

1.继承Thread类实现多线程继承Thread类的方法尽管被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法.start()方法是一个native方法,它将启动一个新线程,并执行run()方法.这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法.例如: [java] view