您的位置:首页 > 编程语言 > Java开发

Java多线程之线程创建和启动

2016-05-31 20:11 387 查看
Java使用Thread类表示线程,所有的线程对象必须是Thread类或其子类的实例,每个线程的作用,实际上就是执行一段程序刘,java使用线程执行体来代表着段程序流

创建线程3方法

继承Thread类

创建线程的方法1是继承Thread类,重写run()方法,该方法的方法体代表了线程需要完成的任务,因此run()方法叫做线程执行体

创建了Thread类子类的实例,即创建了线程对象

启动线程应该用线程对象的start()方法

public class FirstThread extends Thread {
private int i;
@override
public void run(){
//继承Thread时,用this即可获取当前线程
for(;i < 100; i++){
System.out.println(this.getName() + " " + i);
}
}
}

public static void main(String[] args){
for(int i = 0; i < 100; i++){
//用Thread的静态方法获取当前主线程名称
System.out.println(Thread.currentThread().getName());
if(i == 20){
//除主线程,另外启动2个线程
new FirstThread().start();
new FirstThread().start();
}
}
}


实现Runnable接口

创建线程方法2,实现Runnable接口,重写run()方法

创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该对象才是真正的线程对象。通俗来说,就是用Thread来包装实现了Runnable接口的实例

调用start()方法,启动线程

SecondThread st = new SecondThread();
new Thread(st).start();

//也可以指定新线程的名称
new Thread(st,"新线程1").start();

//以上两条线程会共享st实例的变量


public class FirstThread implements Runnable {
private int i;
@override
public void run(){
//实现Runnable接口,只能用Thread.currentThread()获取当前线程
for(;i < 100; i++){
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}


使用Runnable接口创建的多线程可以共享线程类的实例变量。因为这种方式下,程序创建的Runnable对象只是线程的target,而多个线程可以共享一个target

使用Callable和Future创建线程

JDK 1.5后,java.util.concurrent包下的Callable接口,它像是Runnable接口的增强版。Callable接口的call()方法可以作为线程的执行体,比run()方法强大。

那么,完全可以在Thread的target中传入一个Callable对象,执行体是call()方法。但问题在于:Callable不是Runnable的子接口,是JDK1.5新增接口,而且还有返回值

JDK 1.5提供了Future接口来代表Callable接口里call()方法的返回值,FutureTask是Future的实现类,并且实现了Runnable接口,那么可以作为Thread类的target

public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result 返回运行结果
* @throws Exception if unable to compute a result 若不能运算则抛出异常
*/
V call() throws Exception;
}


public interface Future<V> {
//取消该Future关联的Callable任务
boolean cancel(boolean mayInterruptIfRunning);
//如果Callable任务正常完成前被取消,返回true;完成后,不能取消
boolean isCancelled();
//如果Callable任务完成,返回true
boolean isDone();
//返回Callable任务里call()方法的返回值。调用该方法将导致程序阻塞,须等到子线程结束才会得到返回值,也就是异步的
V get() throws InterruptedException, ExecutionException;
//与上一样,不过可以设置最多阻塞timeout时间,时间单位为unit,若超时,抛异常
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}


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

创建Callable接口实现类,并实现call()方法

使用FutureTask类来包装Callable对象,FutureTask封装了Callable的call()方法返回值

创建Thread类的对象,传入FutureTask对象作为target,start()启动线程

使用FutureTask对象的get()方法获取线程返回值

public class ThirdThread {

public static void main(String[] args) {

ThirdThread rt = new ThirdThread();
//FutureTask包装Callable对象
FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() {

@Override
public Integer call() throws Exception {
int i = 0;
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " 循环值" + i);
}
return i;
}
});

for(int i = 0; i < 100; i++){
//用Thread的静态方法获取当前主线程名称
System.out.println(Thread.currentThread().getName() + " 循环值" + i);
if(i == 20){
//除主线程,另外启动1个线程
new Thread(task,"有返回值的线程").start();

try {
System.out.println("子线程返回值 " + task.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}

}
}


//结果部分
有返回值的线程 循环值96
有返回值的线程 循环值97
有返回值的线程 循环值98
有返回值的线程 循环值99
子线程返回值 100
main 循环值21
main 循环值22
main 循环值23
main 循环值24


三种方式对比

继承Thread类,实现Runnable或Callable接口。实现接口实现多线程的方式大体相同

实现接口的优缺点:

Pros:

线程类实现了Runnable或Callable接口,可继承其他类

多个线程可以共享一个target对象(一个Runnable对象),所以适合多个相同线程处理同一个资源,从而将CPU,代码和数据分开,形成清晰的模型

Cons:

编程稍微复杂,若要访问当前线程,只能用Thread.currentThread()

继承Thread的优缺点:

Pros:

编写简单,this即可获得当前线程

Cons:

不能再继承其他类

综上,推荐使用Runnable或Callable接口
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 多线程 线程