线程的创建
创建线程具体来说有三种方式,详细内容如下文.
继承Thread类
详细步骤:
1.定义Thread类的子类,并重写该类的run()方法,run()方法被称为线程执行体.
2.创建Thread子类的实例,即创建线程对象.
3.调用线程对象的start()方法启动该线程.
代码示例:
/**
* Created by dongmengyuan on 18-2-21.
*/
public class FirstThread extends Thread {
//i是FirstThread的实例属性,不是局部变量
private int i;
//重写run()方法,run()方法的方法体是线程执行体
public void run() {
for( ; i < 100; i++) {
//当线程使用继承Thread类时,直接使用this就可以获取当前线程
//Thread对象的getName()返回当前线程的名字
//直接调用getName()方法返回当前线程的名字
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) {
for(int i = 0; i < 100; i++) {
//调用Thread的currentThread()方法获取当前线程
System.out.println(Thread.currentThread().getName() + " " + i);
if(i == 20) {
//创建并启动第一个线程
new FirstThread().start();
//创建并启动第二个线程
new FirstThread().start();
}
}
}
}
上面程序显式的创建了两个线程,但是实际上程序有三个线程,其中包括主线程.
当Java程序开始运行时,程序至少会创建一个主线程,主线程的执行体不是由run()方法确定的,而是由main()方法确定的.
*使用继承Thread类的方法创建线程时,多个线程之间无法共享线程类的实例变量.
实现Runnable接口
详细步骤:
1.定义Runnable接口的实现类,并重写该接口的run()方法.
2.创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象.
3.调用线程对象的start()方法来启动该线程.
代码示例:
/**
* Created by dongmengyuan on 18-2-21.
*/
public class SecondThread implements Runnable {
private int i;
public void run() {
//当线程类实现Runnable接口时,如果想要获取当前线程,只能用Thread.currentThread()方法
System.out.println(Thread.currentThread() + " " + i);
}
public static void main(String[] args) {
for(int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if(i == 20) {
SecondThread st = new SecondThread();
//通过new Thread(target,name)方法创建新线程
new Thread(st, "新线程1").start();
new Thread(st, "新线程2").start();
}
}
}
}
*采用Runnable接口的方式创建的多个线程可以共享线程类的实例属性.因为这种方式下,程序所创建的Runnable对象只是线程的target,多个线程可以共享同一个target.
使用Callable和Future
详细步骤:
1.创建Callable接口的实现类,并实现call()方法,该call()方法作为线程执行体,且该call()方法有返回值.
2.创建Callable实现类的实例,使用Future Task类来包装Callable对象,该Future Task对象封装了Callable对象的call()方法的返回值.
3.使用Future Task对象作为Thread对象的target创建并启动新线程.
4.调用Future Task对象的get()方法来获得子线程执行结束后的返回值.
代码示例:
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
* Created by dongmengyuan on 18-2-22.
*/
public class ThirdThread implements Callable<Integer> {
//实现call()方法,作为线程执行体
//call()方法可以有返回值
public Integer call() {
int i = 0;
for(; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " 的循环变量i的值: " + i);
}
return i;
}
public static void main(String[] args) {
//创建Callable对象
ThirdThread rt = new ThirdThread();
//使用Future Task对象来包括Callable对象
FutureTask<Integer> task = new FutureTask<Integer>(rt);
for(int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " 的循环变量i的值: " + i);
if(i == 20) {
//实质还是以Callable对象来创建并启动线程
new Thread(task,"有返回值的线程").start();
}
}
try {
//获取线程返回值
System.out.println("子线程的返回值:" + task.get());
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
*call()方法比run()方法功能更强大. ---call()方法可以有返回值,可以声明抛出异常.
*Callable接口有泛型限制,Callable接口里的泛型形参类型与call()方法返回值类型相同.
创建线程的三种方式对比
实现Runnable接口和实现Callable接口的方式基本相同,只是Callable接口里定义的方法有返回值,可以声明抛出异常,大体上可以归结为一大类,这种方式与继承Thread方式之间的主要差别是:
采用实现Runnable,Callable接口创建多线程
1.线程类只是实现了Runnable接口或Callable接口,还可以继承其他类
2.多个线程可以共享同一个target对象,非常适合多个相同线程来处理同一份资源的情况.(面向对象)
3.如果要访问当前线程,必须使用Thread.currentThread()方法,比较复杂
采用继承Thread类的方式创建多线程
1.因为线程类已经继承了Thread类,所以不能再继承其他父类
2.如果要访问当前线程,直接使用this即可获得
鉴于上述分析,通常推荐使用实现接口的方式来创建多线程,实现接口的方式比继承类的方式更灵活,也能减少程序之间的耦合度,面向接口编程也是设计模式6大原则的核心.