前言

Java多线程的使用有三种方法:继承Thread类、实现Runnable接口和使用Callable和Future创建线程,本文将对这三种方法一一进行介绍。


1、继承Thread类

实现方式很简单,只需要创建一个类去继承Thread类然后重写run方法,在main方法中调用该类实例对象的start方法即可实现多线程并发。代码:

public class MyThread extends Thread {
@Override
public void run(){
super.run();
System.out.println("执行子线程...");
}
}

测试用例:

public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println("主线程...");
}
}

运行结果:

当然,这里的结果不代表线程的执行顺序,线程是并发执行的,如果多运行几次,打印顺序可能会不一样。多线程的运行过程中,CPU是以不确定的方式去执行线程的,故运行结果与代码的执行顺序或者调用顺序无关,运行结果也可能不一样。关于线程执行的随机性本文后面也有代码示例。

这里还有一个需要注意的点就是main方法中应该调用的是myThread的start方法,而不是run()方法。调用start()方法是告诉CPU此线程已经准备就绪可以执行,进而系统有时间就会来执行其run()方法。而直接调用run()方法,则不是异步执行,而是等同于调用函数般按顺序同步执行,这就失去了多线程的意义了。


2、实现Runnable接口

这种方式的实现也很简单,就是把继承Thread类改为实现Runnable接口。代码如下:

public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("执行子线程...");
}
}

测试用例:

public class Test {
public static void main(String[] args) { Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
System.out.println("主线程运行结束!");
}
}

运行结果:

运行结果没啥好说的,这里main中可以看到真正创建新线程还是通过Thread创建:

Thread thread = new Thread(runnable);

这一步Thread类的作用就是把run()方法包装成线程执行体,然后依然通过start去告诉系统这个线程已经准备好了可以安排执行。


3、使用Callable和Future创建线程

上面的两种方式都有这两个问题:

  1. 无法获取子线程的返回值
  2. run方法不可以抛出异常

为了解决这两个问题,我们就需要用到Callable这个接口了。说到接口,上面的Runnable接口实现类实例是作为Thread类的构造函数的参数传入的,之后通过Thread的start执行run方法中的内容。但是Callable并不是Runnable的子接口,是个全新的接口,它的实例不能直接传入给Thread构造,所以需要另一个接口来转换一下。

Java5提供了Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供了一个实现类FutureTask,该实现类的继承关系如图所示:

可以看到,该实现类不仅实现了Future接口,还实现了Runnable接口,所以可以直接传给Thread构造函数。

而关于FutureTask的构造函数如下:

所以这里面其实就是要比上一个方法再多一个转换过程,最终一样是通过Thread的start来创建新线程。有了这个思路,代码就很容易理解了:

import java.util.concurrent.Callable;

public class MyCallable implements Callable {
int i = 0;
@Override
public Object call() throws Exception {
System.out.println(Thread.currentThread().getName()+" i的值:"+ i);
return i++; //call方法可以有返回值
}
}

测试:

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask; public class Test {
public static void main(String[] args) {
Callable callable = new MyCallable();
for (int i = 0; i < 10; i++) {
FutureTask task = new FutureTask(callable);
new Thread(task,"子线程"+ i).start();
try {
//获取子线程的返回值
System.out.println("子线程返回值:"+task.get() + "\n");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

执行结果(部分):


4、线程执行的随机性

上面介绍第一种方法的时候,说了线程的执行顺序与start()的执行顺序无关,而是CPU有空隙了就过来执行该线程,所以具有随机性,执行顺序也是随机的。

示例代码:

public class MyThread extends Thread {
int i;
public MyThread(int i){
super();
this.i = i;
}
@Override
public void run(){
System.out.println(i);
}
}

测试代码:

public class Test {
Thread thread1 = new MyThread(1);
Thread thread2 = new MyThread(2);
Thread thread3 = new MyThread(3);
Thread thread4 = new MyThread(4);
Thread thread5 = new MyThread(5);
Thread thread6 = new MyThread(6);
Thread thread7 = new MyThread(7);
Thread thread8 = new MyThread(8);
Thread thread9 = new MyThread(9);
Thread thread10 = new MyThread(10); thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
thread6.start();
thread7.start();
thread8.start();
thread9.start();
thread10.start();
}
}

运行结果体现了这一点:


5、三种方式的对比

第一种和后面两种的对比:

1、通过代码可以看出,第一种方法是最简洁方便的,直接就可以start,不需要任何转换

2、但是第一种有一个很不好的地方就是继承了Thread类后由于java的单继承机制,就不可以继承其他的类了,而如果实现的是接口,就可以实现多个接口,使开发更灵活。

第二种和第三种方式对比:

1、同样的,第二种方法相对第三种方式来说代码更简洁,使用更方便,少了一次转换

2、第三种方法有两个优点:有返回值、可以抛出异常

总结

实际开发中可能有更复杂的代码实现,需要继承其他的类,所以平时更推荐通过实现接口来实现多线程,也就是通过第二或第三种方式来实现,这样能保持代码灵活和解耦。

而选择第二还是第三种方式,则要根据run()方法是不是需要返回值或者捕获异常来决定,如果不需要,可以选择用第二种方式实现,代码更简洁。

博客园地址:http://www.cnblogs.com/zivwong

欢迎转载,请在明显位置给出出处及链接

Java多线程1:使用多线程的几种方式以及对比的更多相关文章

  1. IOS 多线程,线程同步的三种方式

    本文主要是讲述 IOS 多线程,线程同步的三种方式,更多IOS技术知识,请登陆疯狂软件教育官网. 一般情况下我们使用线程,在多个线程共同访问同一块资源.为保护线程资源的安全和线程访问的正确性. 在IO ...

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

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

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

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

  4. java 中创建线程有哪几种方式?

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

  5. Java反射获取class对象的三种方式,反射创建对象的两种方式

    Java反射获取class对象的三种方式,反射创建对象的两种方式 1.获取Class对象 在 Java API 中,提供了获取 Class 类对象的三种方法: 第一种,使用 Class.forName ...

  6. java多线程之创建线程的4种方式及Future

    Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例.Java可以用四种方式来创建线程: 继承Thread创建线程 实现Runnable接口创建线程 实现callab ...

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

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

  8. Java中 单例(Singleton)的两种方式

    第一种(饿汉式单例模式):在声明变量时实例化 public class Singleton { //静态初始化自动实例化 private static Singleton instance = new ...

  9. Java使用基本字节流OutputStream的四种方式对于数据复制(文本,音视频,图像等数据)

    //package 字符缓冲流bufferreaderDemo; import java.io.BufferedOutputStream; import java.io.FileInputStream ...

随机推荐

  1. B/S结构-登录页面-测试用例设计

    页面描述: 有一个登陆页面, 假如上面有2个textbox, 一个提交按钮 测试需求: 请针对这个页面设计30个以上的testcase 功能测试(Function test) 0. 什么都不输入,点击 ...

  2. 【Python开发】urllib2.urlopen超时问题

    原帖地址:http://hi.baidu.com/yss1983/item/933fbe45a09c43e01381da06 问题描述:     没有设置timeout参数,结果在网络环境不好的情况下 ...

  3. C# XML操作之一:使用XmlDocument来读写

    所有代码都在同一个类中,含有对象 XmlDocument doc = new XmlDocument(); 新建XML,并且写入内容 private void button4_Click(object ...

  4. Spring MVC (二)注解式开发使用详解

    MVC注解式开发即处理器基于注解的类开发, 对于每一个定义的处理器, 无需在xml中注册. 只需在代码中通过对类与方法的注解, 即可完成注册. 定义处理器 @Controller: 当前类为处理器 @ ...

  5. nslookup 命令

    NAME nslookup - query Internet name servers interactively SYNOPSIS nslookup [-option] [name | -] [se ...

  6. PTA(Advanced Level)1031.Hello World for U

    Given any string of N (≥5) characters, you are asked to form the characters into the shape of U. For ...

  7. PostgreSQL中with和without time zone两者有什么区别

    with和without time zone两者有什么区别 1.区别 1)名字上看一个是带时区的,另一个是不带时区的,查出来的时间是一样的,只是一个带时区标志,一个不带而已,时区的基准是格林威治时间U ...

  8. GitHub从小白到熟悉<三>

    上传文件

  9. MySQL_入手<一>增--数据库操作

    创建数据库 create database db_sanguo charset utf8; 切进db_sanguo use db_sanguo 创建英雄 create table t_hero( id ...

  10. 剑指offer-数组中只出现一次的数字-数组-python

    题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字.   # -*- coding:utf-8 -*- class Solution: # 返回[a, ...