一、前言

java多线程实现的三种方式有继承thread类,实现runnable接口,使用exectorservice、callable、future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。

二、继承thread类实现多线程

1.thread本质上也是实现了runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过thread类的start()实例方法。

2.start()方法是一个native方法,它将启动一个新线程,并执行run()方法

3.这种方式实现多线程很简单,通过自己的类直接extend thread,并重写run()方法,就可以启动新线程并执行自己定义的run()方法

class mythread extends thread{
    public void run(){
        system.out.println("my thread.run()");
    }
}

启动线程:

mythread mythread1 = new mythread();
mythread1.start();

三、 runnable接口方式实现多线程

java程序里面对于继承永远都是存在有单继承局限的,如果自己的类已经extends另一个类,就无法直接extends thread,java里面又提供第二种多线程的主体定义结构形式:实现java.lang.runnable接口

定义:

@functionalinterface    // 从jdk1.8引入了lambda 表达式之后就变为了函数式接口
public interface runnable {
  public void run();
}

实现一个runnable接口:

public class mythread extends otherclass implements runnable {
  public void run() {
   system.out.println("mythread.run()");
  }
}

启动mythread,首先实例化一个thread,并传入自己的mythread实例:

mythread mythread = new mythread();
thread thread = new thread(mythread);
thread.start();

当传入一个runnable target参数给thread后,thread的run()方法就会调用target.run()

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

四、thread和runnable的关系

1.从代码的结构本身来讲肯定使用runnable是最方便的,因为其可以避免单继承的局限,同时也可以更好的进行功能的扩充

2.从结构上观察thread与runnable的联系

 public class thread extends object implements runnable{}
thread类也是runnable 接口的子类,那么在之前继承thread类的时候实际上覆写的还是runnable的方法。

3.进行thread启动多线程时调用的是start()方法,而后找到的是run()方法。当通过thread类的构造方法传递了一个runnable接口对象的时候,该接口对象将被thread中的target的属性保存,在start()方法执行的时候会调用thread类的run方法,而这个run()方法去调用runnable接口子类被覆写过的run()方法。

多线程开发的本质实质上是在于多个线程可以进行统一资源的抢占,那么thread主要描述的是线程,那么资源的描述是通过runnable完成的。

五、使用executorservice、callable、future实现有返回结果的多线程

1.executorservice、callable、future这个对象实际上都是属于executor框架中的功能类

2.返回值的任务必须实现callable接口,类似的,无返回值的任务必须runnable接口

3.执行callable任务后,可以获取一个future的对象,在该对象上调用get就可以获取到callable任务返回的object,再结合线程池接口executorservice就可以实现有返回结果的多线程了

runnable接口有一个缺点:当线程执行完毕后,我们无法获取一个返回值,所以从jdk1.5之后就提出了一个新的线程实现接口:java.util.concurrent.callable接口

@functionalinterface
public interface callable<v> {
 public v call() throws exception;
}

callbale定义的时候可以设置一个泛型,此泛型的类型就是返回数据的类型

callable接口和runnable接口是类似的,但是需要实现的是call方法,而且从上面的代码中我们可以看到run()方法执行的任务是没有返回值的,但是call方法有返回值,可以自定义返回值的类型,这就是两个接口最大的区别

例子:

import java.util.concurrent.*;
import java.util.date;
import java.util.list;
import java.util.arraylist;
 
/**
* 有返回值的线程
*/
@suppresswarnings("unchecked")
public class test {
	public static void main(string[] args) throws executionexception,
	   interruptedexception {
	   system.out.println("----程序开始运行----");
	   date date1 = new date();
	 
	   int tasksize = 5;
	   // 创建一个线程池
	   executorservice pool = executors.newfixedthreadpool(tasksize);
	   // 创建多个有返回值的任务
	   list<future> list = new arraylist<future>();
	   for (int i = 0; i < tasksize; i++) {
	    callable c = new mycallable(i + " ");
	    // 执行任务并获取future对象
	    future f = pool.submit(c);
	    // system.out.println(">>>" + f.get().tostring());
	    list.add(f);
	   }
	   // 关闭线程池
	   pool.shutdown();
	 
	   // 获取所有并发任务的运行结果
	   for (future f : list) {
	    // 从future对象上获取任务的返回值,并输出到控制台
	    system.out.println(">>>" + f.get().tostring());
	   }
	 
	   date date2 = new date();
	   system.out.println("----程序结束运行----,程序运行时间【"
	     + (date2.gettime() - date1.gettime()) + "毫秒】");
	}
}
 
class mycallable implements callable<object> {
	private string tasknum;
	 
	mycallable(string tasknum) {
	   this.tasknum = tasknum;
	}
	 
	public object call() throws exception {
	   system.out.println(">>>" + tasknum + "任务启动");
	   date datetmp1 = new date();
	   thread.sleep(1000);
	   date datetmp2 = new date();
	   long time = datetmp2.gettime() - datetmp1.gettime();
	   system.out.println(">>>" + tasknum + "任务终止");
	   return tasknum + "任务返回运行结果,当前任务时间【" + time + "毫秒】";
	}
}

到此这篇关于java基础之多线程的三种实现方式的文章就介绍到这了,更多相关java多线程的实现方式内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!